MDN states that async iterators have a return method
const asyncIterable = {
[Symbol.asyncIterator]() {
let i = 0;
return {
next() {
const done = i === LIMIT;
const value = done ? undefined : i++;
return Promise.resolve({ value, done });
},
return() {
// This will be reached if the consumer called 'break' or 'return' early in the loop.
return { done: true };
}
};
}
};
However, the Typescript definitions of async iterators require the return
method to
{value: someValue, done: true}
, whereas MDN does not do this.Here's the TS definition:
interface AsyncIterator<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]): Promise<IteratorResult<T, TReturn>>;
return?(value?: TReturn | PromiseLike<TReturn>): Promise<IteratorResult<T, TReturn>>;
throw?(e?: any): Promise<IteratorResult<T, TReturn>>;
}
Can someone explain the discrepancy? How does return
work?
MDN, however authoritative, is not the source of truth when it comes to language specifications. TypeScript follows the ECMAScript spec, in this instance, the definition of the AsyncIterator
interface (as @@asyncIterator
well-known symbol is a method that returns an AsyncIterator
).
Table 76 and 77 of the spec provide, respectively, the required and optional properties of the interface: next
, return
, and throw
. We are interested in what the spec has to say about the parameters of the second one:
The returned promise will fulfill with an
IteratorResult
object which will typically have a "done" property whose value is true, and a "value" property with the value passed as the argument of the return method. However, this requirement is not enforced.
Note the last part of the first sentence — here is where the TReturn | PromiseLike<TReturn>
comes from. Then note the second sentence — this is why the parameter is optional.
As you can also see from the above, the return
method returns an object implementing the IteratorResult
interface which is defined to have a done
boolean and a value
unrestricted property. Both properties are not considered optional, but each has a note in table 78 of the spec that allows for either to be missing.
However, TypeScript definition for the IteratorReturnResult
(a member of the IteratorResult
union type) does not take into account those notes and marks those properties as required:
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}
This has been called into question in issue #8938 on the source repository with the team reasoning being:
As noted by @ivogabe we need to have boolean literal types, to be able to model this one accurately.
and subsequently closed in favor of issue #2983 fixed by PR #30790 with the original request seemingly slipping through the cracks.