I'm using a generator in my code, that returns strings, but I'm facing the following problem:
const pageUrl = generator.next().value;
const result = catalogPageParser(pageUrl);
Returning the error:
Argument of type 'string | void' is not assignable to parameter of type 'string'.
Type 'void' is not assignable to type 'string'.ts(2345)
This is easily fixed by either modify the variable like this:
const pageUrl = generator.next().value ?? "some valid url";
Or by modifying the signature of the catalogPageParser
function. I do not like any of those solutions, but I cannot think of anything else. Any ideas about how to type that correctly?
I do not like the default value option because there's not a reasonable default value in my case. I also do not like the second because it does not make at all for the function to accept void as a possible argument.
Example
function* someGenerator() {
yield "url1";
yield "url2";
}
function acceptsStringOnly(argument: string) {
console.log(argument);
}
const value = someGenerator().next().value;
acceptsStringOnly(value);
CodePudding user response:
It is because the result of an iterator can have no value if it has already ended. In those cases the property done
is false
. TypeScript can automatically narrow the type of the value
property if you check done
:
function* generator() {
yield 'string1';
yield 'string2';
yield 'string3';
}
const iterator = generator();
const result = iterator.next();
let theString: string;
// This fails as in your example
theString = result.value;
if (!result.done) {
// this works
theString = result.value;
} else {
// whatever you have to do if the iterator has ended
}
So the solution to your case can be: 1) check first if done
is true
and act accordingly (early return, throw, whatever you need); 2) if you know that the iterator will always return a value you can use a non-null assertion.
Your current solution using ??
is likely a good one also, but it of course depends on your needs. One disadvantage I can think of would be that you don't have a proper default value in that context. Managing the case that iterator has ended is, IMHO, a better approach in the general case.