Repository navigation
Closed
Labels
BugA bug in TypeScriptA bug in TypeScriptDomain: lib.d.tsThe issue relates to the different libraries shipped with TypeScriptThe issue relates to the different libraries shipped with TypeScript
Milestone
Description
TypeScript Version: 3.6.2
Search Terms:
async iterator iterable next
Code:
filename: constant.ts
class ConstantIterator<T> implements AsyncIterator<T, undefined, T | undefined> {
constructor(private constant: T) {
}
async next(value?: T): Promise<IteratorResult<T>> {
if (value != null) {
throw new Error("ConstantIterator.prototype.next may not take any values");
}
return {value: this.constant, done: false};
}
}Expected behavior:
Code compiles.
Actual behavior:
constant.ts(4,9): error TS2416: Property 'next' in type 'ConstantIterator<T>' is not assignable to the same property in base type 'AsyncIterator<T, undefined, T>'.
Type '(value?: T) => Promise<{ value: T; done: boolean; }>' is not assignable to type '(...args: [] | [T | PromiseLike<T>]) => Promise<IteratorResult<T, undefined>>'.
Types of parameters 'value' and 'args' are incompatible.
Type '[] | [T | PromiseLike<T>]' is not assignable to type '[T?]'.
Type '[T | PromiseLike<T>]' is not assignable to type '[T?]'.
Types of property '0' are incompatible.
Type 'T | PromiseLike<T>' is not assignable to type 'T'.
'T | PromiseLike<T>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.
Type 'PromiseLike<T>' is not assignable to type 'T'.
'PromiseLike<T>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.Note that the synchronous version of ConstantIterator does not throw the same error. It’s the type union of T | PromiseLike<T> which seems to cause the error.
Playground Link:
Playground has yet to be updated for 3.6.
Related Issues:
#30790 Strongly typed iterator PR.
#32682 Other (async) iterator usability issues.
#33131 Possible change to type inference in 3.6.
Metadata
Metadata
Assignees
Labels
BugA bug in TypeScriptA bug in TypeScriptDomain: lib.d.tsThe issue relates to the different libraries shipped with TypeScriptThe issue relates to the different libraries shipped with TypeScript
Activity
ilogico commentedon Sep 5, 2019
The error message has a lot of text, but it's clear:
Promise<IteratorResult<T>>and notIteratorResult<T>T | undefined, but alsoPromiseLike<T | undefined>brainkim commentedon Sep 5, 2019
Gonçalo Fernandes (@ilogico) Woops, I forgot to change the return type when I was testing that this error does not happen for sync iterators.
try running tsc version 3.6.2 on the above code.
Is this an actual requirement in the async iterator spec? Can you point to where this is stated? As far as I can tell I have never defined an async iterator which expected a promise as the result of a yield.
fatcerberus commentedon Sep 5, 2019
Based on a quick test in Node.js, there is no special handling for passing promises to
.next():The promise is not unwrapped by
yield(unless explicitlyawaited).brainkim commentedon Sep 5, 2019
Yeah I initially thought it was an assignability error related to the weird tuple args, but I think it’s probably the fact that
TNextshould never be unwrapped as a Promise. Likely just a misunderstanding of async iterators?brainkim commentedon Sep 6, 2019
Nathan Shively-Sanders (@sandersn) Can you explain the addition of the “Working as Intended” label? Here’s the current
AsyncIteratorinterface defined by typescript:Requiring the
nextmethod to have a first argument of typeTNext | PromiseLike<TNext>implies that all well-formed async iterators unwrap the value passed tonext, and this is a requirement which even async generators do not adhere to. It is strictly incorrect and I can point to the exact parts of the spec which prove this.The note following the definition states (emphasis mine):
These are all the definitions which relate to the
TNexttype parameter as it relates to async generators. The methodAsyncGenerator.prototype.nextcallsAsyncGeneratorEnqueuewithTNextas a normal completion,AsyncGeneratorEnqueueadds the completion to a queue without reading or awaiting it, andAsyncGeneratorResumeNextpulls the completion from the queue, and if the completion is not abrupt (which it won’t be because anextcall never enqueues an abrupt completion), it resumes the generator with the completion as the value of yield. See step 18 ofAsyncGeneratorResumeNext:At no point in any of these steps is the
TNextvalue awaited, and this is proven to be true in the comment above.The correct definition of
AsyncIteratorshould probably be:In other words, the same as
Iteratorexcept the return types are all promises.You could make an argument that the value passed to
returnshould beTReturn | PromiseLike<TReturn>based on step 10 ofAsyncGeneratorResumeNextand 25.5.3.5.1 AsyncGeneratorResumeNext Return Processor Fulfilled Functions which does state that promises passed toAsyncGenerator.prototype.returnare unwrapped, but theAsyncIteratorinterface itself as defined by the spec does not contain any directives that all async iterators should unwrap the value passed toreturn.In any case, the value passed to
nextis not required to be unwrapped anywhere in the spec for both async iterators and async generators.ilogico commentedon Sep 6, 2019
I agree that requiring the next method to accept
PromiseLike<TNext>is incorrect.If some iterators allow it, they should declare it explicitly.
fatcerberus commentedon Sep 6, 2019
To be 100% clear: if a
Promise<string>is passed tonext, then inferringTNext = stringis wrong; the correct inference isPromise<string>, because that’s what will be observed by the generator.sandersn commentedon Sep 6, 2019
Brian Kim (@brainkim) I understood your comment
To refer to your misunderstanding. But it sounds like it's a misunderstanding by the author of lib.d.ts?
brainkim commentedon Sep 6, 2019
Nathan Shively-Sanders (@sandersn) Misunderstandings all around! Yes, I think lib.d.ts was written with a slight misunderstanding of async iterators.
brainkim commentedon Sep 10, 2019
Would it be possible to fix this in a 3.6 (3.6.4?) release? Currently assigning/implementing async iterators is really broken on 3.6 and I don’t like idea of telling people you have to skip a version of typescript just to use them.
I would also be willing to volunteer time to get this and other (async) iterator issues fixed! Let me know if you’re willing to accept non-microsoft contributions for these issues.
rbuckton commentedon Sep 10, 2019
You are correct that
nextandreturndo not await the values passes back to the async generator. I am looking into fixing the types now.