I'm using typescript and I created the following code
function* myIter(): Iterator<number> {
yield 3;
yield 2;
yield 1;
}
const iter: Iterator<number> = myIter();
const item:IteratorYieldResult<number> = iter.next();
DEMO
It produces a typescript error

Telling me on hover what the error is

I checked what exactly next is returning and found this interface definition:
interface Iterator<T, TReturn = any, TNext = undefined> {
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return?(value?: TReturn): IteratorResult<T, TReturn>;
throw?(e?: any): IteratorResult<T, TReturn>;
}
And this is the point where I get lost, because my iterator is defined as Iteractor<number> so what is TReturn. On the other hand it is by default set to any so I guess it is optional and I can leave it out. But for some reason it doesn't work that way with IteratorResult. Anyway, some guidance would be appreciated?
I'm unsure what the problem is here, but maybe someone can help me out with this one?
As you've shown, the next() method of an iterator returns a value of type IteratorResult. This is defined as:
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T>
| IteratorReturnResult<TReturn>;
, a union of IteratorYieldResult and IteratorReturnResult, themselves defined like this:
interface IteratorYieldResult<TYield> {
done?: false;
value: TYield;
}
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}
That means the compiler is telling you that iter.next() is returning either an IteratorYieldResult object with a false/missing done property, and a value of type number, or it is returning an IteratorReturnResult object with a true done property, and a value of type any.
You are assigning that to a variable of type IteratorYieldResult, and the compiler is telling you "wait, it might not be that type. It could be an IteratorReturnResult".
Now, since you wrote the generator yourself, you know that the first time you call next() you're going to get an IteratorYieldResult because there are three yield statements before the function returns. But that's information the type system just doesn't have. It's like this:
function foo(): number | string {
return "hello";
}
const str: string = foo(); // error! number not assignable to string
The function foo() returns either a number or a string, according to its type signature. You can tell that it will always return a string, but the compiler cannot tell this, because it's just looking at the signature, not drilling down into the implementation. And so it complains: "hey, maybe str will be a number at runtime, not a string as you are claiming".
So what's the "right" thing to do here? I guess it depends on what you're trying to do. In this toy example, you can just tell the compiler that is doesn't need to worry and that you are sure to get an IteratorYieldResult and not an IteratorReturnResult. And this sort of "telling" the compiler that something is of a narrower type than it can verify is done via a type assertion:
const itemAsserted = iter.next() as IteratorYieldResult<number>; // okay
Or, you can do the general check where you don't claim to know what's coming out (which makes sense if the implementation of myIter() might change, or if you're doing iter.next() in a loop):
const itemSafe = iter.next();
// const itemSafe: IteratorResult<number, any>
if (!itemSafe.done) {
itemSafe; // const itemSafe: IteratorYieldResult<number>
itemSafe.value.toFixed(); // okay
}
Here we are just letting the compiler tell us that iter.next() returns an IteratorResult<number, any>. Then we take that returned value and test it by checking the doneproperty. If it's not true, then the compiler helpfully narrows the result down to IteratorYieldResult<number> for you. You don't need to assert anything because the compiler understands the kind of type checking done above; it's because IteratorResult is a discriminated union, where a single property (in this case done) can be checked to figure out which member of the union you're looking at.
Okay, I hope that makes sense to you and is helpful. Good luck!
Playground link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With