Skip to content

Commit 344dba8

Browse files
authored
Fix incorrect parameter types for AsyncIterator next/return (#33354)
1 parent 56e2cb3 commit 344dba8

11 files changed

+143
-14
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28740,11 +28740,13 @@ namespace ts {
2874028740
let nextType: Type | undefined;
2874128741
if (methodName !== "throw") {
2874228742
const methodParameterType = methodParameterTypes ? getUnionType(methodParameterTypes) : unknownType;
28743-
const resolvedMethodParameterType = resolver.resolveIterationType(methodParameterType, errorNode) || anyType;
2874428743
if (methodName === "next") {
28745-
nextType = resolvedMethodParameterType;
28744+
// The value of `next(value)` is *not* awaited by async generators
28745+
nextType = methodParameterType;
2874628746
}
2874728747
else if (methodName === "return") {
28748+
// The value of `return(value)` *is* awaited by async generators
28749+
const resolvedMethodParameterType = resolver.resolveIterationType(methodParameterType, errorNode) || anyType;
2874828750
returnTypes = append(returnTypes, resolvedMethodParameterType);
2874928751
}
2875028752
}

src/lib/es2018.asyncgenerator.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
44
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
5-
next(...args: [] | [TNext | PromiseLike<TNext>]): Promise<IteratorResult<T, TReturn>>;
5+
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
66
return(value: TReturn | PromiseLike<TReturn>): Promise<IteratorResult<T, TReturn>>;
77
throw(e: any): Promise<IteratorResult<T, TReturn>>;
88
[Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;

src/lib/es2018.asynciterable.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ interface SymbolConstructor {
1111

1212
interface AsyncIterator<T, TReturn = any, TNext = undefined> {
1313
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
14-
next(...args: [] | [TNext | PromiseLike<TNext>]): Promise<IteratorResult<T, TReturn>>;
14+
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
1515
return?(value?: TReturn | PromiseLike<TReturn>): Promise<IteratorResult<T, TReturn>>;
1616
throw?(e?: any): Promise<IteratorResult<T, TReturn>>;
1717
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//// [customAsyncIterator.ts]
2+
// GH: https://github.com/microsoft/TypeScript/issues/33239
3+
class ConstantIterator<T> implements AsyncIterator<T, undefined, T | undefined> {
4+
constructor(private constant: T) {
5+
}
6+
async next(value?: T): Promise<IteratorResult<T>> {
7+
if (value != null) {
8+
throw new Error("ConstantIterator.prototype.next may not take any values");
9+
}
10+
return { value: this.constant, done: false };
11+
}
12+
}
13+
14+
//// [customAsyncIterator.js]
15+
// GH: https://github.com/microsoft/TypeScript/issues/33239
16+
class ConstantIterator {
17+
constructor(constant) {
18+
this.constant = constant;
19+
}
20+
async next(value) {
21+
if (value != null) {
22+
throw new Error("ConstantIterator.prototype.next may not take any values");
23+
}
24+
return { value: this.constant, done: false };
25+
}
26+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/compiler/customAsyncIterator.ts ===
2+
// GH: https://github.com/microsoft/TypeScript/issues/33239
3+
class ConstantIterator<T> implements AsyncIterator<T, undefined, T | undefined> {
4+
>ConstantIterator : Symbol(ConstantIterator, Decl(customAsyncIterator.ts, 0, 0))
5+
>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23))
6+
>AsyncIterator : Symbol(AsyncIterator, Decl(lib.es2018.asynciterable.d.ts, --, --))
7+
>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23))
8+
>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23))
9+
10+
constructor(private constant: T) {
11+
>constant : Symbol(ConstantIterator.constant, Decl(customAsyncIterator.ts, 2, 16))
12+
>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23))
13+
}
14+
async next(value?: T): Promise<IteratorResult<T>> {
15+
>next : Symbol(ConstantIterator.next, Decl(customAsyncIterator.ts, 3, 5))
16+
>value : Symbol(value, Decl(customAsyncIterator.ts, 4, 15))
17+
>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23))
18+
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
19+
>IteratorResult : Symbol(IteratorResult, Decl(lib.es2015.iterable.d.ts, --, --))
20+
>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23))
21+
22+
if (value != null) {
23+
>value : Symbol(value, Decl(customAsyncIterator.ts, 4, 15))
24+
25+
throw new Error("ConstantIterator.prototype.next may not take any values");
26+
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
27+
}
28+
return { value: this.constant, done: false };
29+
>value : Symbol(value, Decl(customAsyncIterator.ts, 8, 16))
30+
>this.constant : Symbol(ConstantIterator.constant, Decl(customAsyncIterator.ts, 2, 16))
31+
>this : Symbol(ConstantIterator, Decl(customAsyncIterator.ts, 0, 0))
32+
>constant : Symbol(ConstantIterator.constant, Decl(customAsyncIterator.ts, 2, 16))
33+
>done : Symbol(done, Decl(customAsyncIterator.ts, 8, 38))
34+
}
35+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
=== tests/cases/compiler/customAsyncIterator.ts ===
2+
// GH: https://github.com/microsoft/TypeScript/issues/33239
3+
class ConstantIterator<T> implements AsyncIterator<T, undefined, T | undefined> {
4+
>ConstantIterator : ConstantIterator<T>
5+
6+
constructor(private constant: T) {
7+
>constant : T
8+
}
9+
async next(value?: T): Promise<IteratorResult<T>> {
10+
>next : (value?: T) => Promise<IteratorResult<T, any>>
11+
>value : T
12+
13+
if (value != null) {
14+
>value != null : boolean
15+
>value : T
16+
>null : null
17+
18+
throw new Error("ConstantIterator.prototype.next may not take any values");
19+
>new Error("ConstantIterator.prototype.next may not take any values") : Error
20+
>Error : ErrorConstructor
21+
>"ConstantIterator.prototype.next may not take any values" : "ConstantIterator.prototype.next may not take any values"
22+
}
23+
return { value: this.constant, done: false };
24+
>{ value: this.constant, done: false } : { value: T; done: false; }
25+
>value : T
26+
>this.constant : T
27+
>this : this
28+
>constant : T
29+
>done : false
30+
>false : false
31+
}
32+
}

tests/baselines/reference/types.asyncGenerators.es2018.1.symbols

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,13 @@ async function * awaitedType2() {
289289
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
290290
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
291291
}
292+
async function * nextType1(): { next(...args: [] | [number | PromiseLike<number>]): any } {
293+
>nextType1 : Symbol(nextType1, Decl(types.asyncGenerators.es2018.1.ts, 122, 1))
294+
>next : Symbol(next, Decl(types.asyncGenerators.es2018.1.ts, 123, 31))
295+
>args : Symbol(args, Decl(types.asyncGenerators.es2018.1.ts, 123, 37))
296+
>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --))
297+
298+
const x = yield; // `number | PromiseLike<number>` (should not await TNext)
299+
>x : Symbol(x, Decl(types.asyncGenerators.es2018.1.ts, 124, 9))
300+
}
292301

tests/baselines/reference/types.asyncGenerators.es2018.1.types

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,4 +430,13 @@ async function * awaitedType2() {
430430
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
431431
>1 : 1
432432
}
433+
async function * nextType1(): { next(...args: [] | [number | PromiseLike<number>]): any } {
434+
>nextType1 : () => { next(...args: [] | [number | PromiseLike<number>]): any; }
435+
>next : (...args: [] | [number | PromiseLike<number>]) => any
436+
>args : [] | [number | PromiseLike<number>]
437+
438+
const x = yield; // `number | PromiseLike<number>` (should not await TNext)
439+
>x : number | PromiseLike<number>
440+
>yield : number | PromiseLike<number>
441+
}
433442

tests/baselines/reference/types.asyncGenerators.es2018.2.errors.txt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(
33
tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(10,7): error TS2322: Type '() => AsyncGenerator<string, void, undefined>' is not assignable to type '() => AsyncIterableIterator<number>'.
44
Type 'AsyncGenerator<string, void, undefined>' is not assignable to type 'AsyncIterableIterator<number>'.
55
Types of property 'next' are incompatible.
6-
Type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<number, any>>'.
6+
Type '(...args: [] | [undefined]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [undefined]) => Promise<IteratorResult<number, any>>'.
77
Type 'Promise<IteratorResult<string, void>>' is not assignable to type 'Promise<IteratorResult<number, any>>'.
88
Type 'IteratorResult<string, void>' is not assignable to type 'IteratorResult<number, any>'.
99
Type 'IteratorYieldResult<string>' is not assignable to type 'IteratorResult<number, any>'.
@@ -14,15 +14,15 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(
1414
tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(16,7): error TS2322: Type '() => AsyncGenerator<string, void, unknown>' is not assignable to type '() => AsyncIterableIterator<number>'.
1515
Type 'AsyncGenerator<string, void, unknown>' is not assignable to type 'AsyncIterableIterator<number>'.
1616
Types of property 'next' are incompatible.
17-
Type '(...args: [] | [unknown]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<number, any>>'.
17+
Type '(...args: [] | [unknown]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [undefined]) => Promise<IteratorResult<number, any>>'.
1818
Type 'Promise<IteratorResult<string, void>>' is not assignable to type 'Promise<IteratorResult<number, any>>'.
1919
tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(19,7): error TS2322: Type '() => AsyncGenerator<string, void, undefined>' is not assignable to type '() => AsyncIterable<number>'.
2020
Type 'AsyncGenerator<string, void, undefined>' is not assignable to type 'AsyncIterable<number>'.
2121
Types of property '[Symbol.asyncIterator]' are incompatible.
2222
Type '() => AsyncGenerator<string, void, undefined>' is not assignable to type '() => AsyncIterator<number, any, undefined>'.
2323
Type 'AsyncGenerator<string, void, undefined>' is not assignable to type 'AsyncIterator<number, any, undefined>'.
2424
Types of property 'next' are incompatible.
25-
Type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<number, any>>'.
25+
Type '(...args: [] | [undefined]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [undefined]) => Promise<IteratorResult<number, any>>'.
2626
Type 'Promise<IteratorResult<string, void>>' is not assignable to type 'Promise<IteratorResult<number, any>>'.
2727
tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(22,7): error TS2322: Type '() => AsyncGenerator<string, void, undefined>' is not assignable to type '() => AsyncIterable<number>'.
2828
Type 'AsyncGenerator<string, void, undefined>' is not assignable to type 'AsyncIterable<number>'.
@@ -32,7 +32,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(
3232
Type '() => AsyncGenerator<string, void, unknown>' is not assignable to type '() => AsyncIterator<number, any, undefined>'.
3333
Type 'AsyncGenerator<string, void, unknown>' is not assignable to type 'AsyncIterator<number, any, undefined>'.
3434
Types of property 'next' are incompatible.
35-
Type '(...args: [] | [unknown]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<number, any>>'.
35+
Type '(...args: [] | [unknown]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [undefined]) => Promise<IteratorResult<number, any>>'.
3636
Type 'Promise<IteratorResult<string, void>>' is not assignable to type 'Promise<IteratorResult<number, any>>'.
3737
tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(28,7): error TS2322: Type '() => AsyncGenerator<string, void, undefined>' is not assignable to type '() => AsyncIterator<number, any, undefined>'.
3838
Type 'AsyncGenerator<string, void, undefined>' is not assignable to type 'AsyncIterator<number, any, undefined>'.
@@ -53,7 +53,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(
5353
tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(67,42): error TS2741: Property '[Symbol.iterator]' is missing in type 'AsyncGenerator<any, any, any>' but required in type 'Iterable<number>'.
5454
tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(70,42): error TS2322: Type 'AsyncGenerator<number, any, undefined>' is not assignable to type 'Iterator<number, any, undefined>'.
5555
Types of property 'next' are incompatible.
56-
Type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<number, any>>' is not assignable to type '(...args: [] | [undefined]) => IteratorResult<number, any>'.
56+
Type '(...args: [] | [undefined]) => Promise<IteratorResult<number, any>>' is not assignable to type '(...args: [] | [undefined]) => IteratorResult<number, any>'.
5757
Type 'Promise<IteratorResult<number, any>>' is not assignable to type 'IteratorResult<number, any>'.
5858
Property 'value' is missing in type 'Promise<IteratorResult<number, any>>' but required in type 'IteratorYieldResult<number>'.
5959
tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(74,12): error TS2504: Type '{}' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.
@@ -79,7 +79,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(
7979
!!! error TS2322: Type '() => AsyncGenerator<string, void, undefined>' is not assignable to type '() => AsyncIterableIterator<number>'.
8080
!!! error TS2322: Type 'AsyncGenerator<string, void, undefined>' is not assignable to type 'AsyncIterableIterator<number>'.
8181
!!! error TS2322: Types of property 'next' are incompatible.
82-
!!! error TS2322: Type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<number, any>>'.
82+
!!! error TS2322: Type '(...args: [] | [undefined]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [undefined]) => Promise<IteratorResult<number, any>>'.
8383
!!! error TS2322: Type 'Promise<IteratorResult<string, void>>' is not assignable to type 'Promise<IteratorResult<number, any>>'.
8484
!!! error TS2322: Type 'IteratorResult<string, void>' is not assignable to type 'IteratorResult<number, any>'.
8585
!!! error TS2322: Type 'IteratorYieldResult<string>' is not assignable to type 'IteratorResult<number, any>'.
@@ -98,7 +98,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(
9898
!!! error TS2322: Type '() => AsyncGenerator<string, void, unknown>' is not assignable to type '() => AsyncIterableIterator<number>'.
9999
!!! error TS2322: Type 'AsyncGenerator<string, void, unknown>' is not assignable to type 'AsyncIterableIterator<number>'.
100100
!!! error TS2322: Types of property 'next' are incompatible.
101-
!!! error TS2322: Type '(...args: [] | [unknown]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<number, any>>'.
101+
!!! error TS2322: Type '(...args: [] | [unknown]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [undefined]) => Promise<IteratorResult<number, any>>'.
102102
!!! error TS2322: Type 'Promise<IteratorResult<string, void>>' is not assignable to type 'Promise<IteratorResult<number, any>>'.
103103
yield* (async function * () { yield "a"; })();
104104
};
@@ -110,7 +110,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(
110110
!!! error TS2322: Type '() => AsyncGenerator<string, void, undefined>' is not assignable to type '() => AsyncIterator<number, any, undefined>'.
111111
!!! error TS2322: Type 'AsyncGenerator<string, void, undefined>' is not assignable to type 'AsyncIterator<number, any, undefined>'.
112112
!!! error TS2322: Types of property 'next' are incompatible.
113-
!!! error TS2322: Type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<number, any>>'.
113+
!!! error TS2322: Type '(...args: [] | [undefined]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [undefined]) => Promise<IteratorResult<number, any>>'.
114114
!!! error TS2322: Type 'Promise<IteratorResult<string, void>>' is not assignable to type 'Promise<IteratorResult<number, any>>'.
115115
yield "a";
116116
};
@@ -128,7 +128,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(
128128
!!! error TS2322: Type '() => AsyncGenerator<string, void, unknown>' is not assignable to type '() => AsyncIterator<number, any, undefined>'.
129129
!!! error TS2322: Type 'AsyncGenerator<string, void, unknown>' is not assignable to type 'AsyncIterator<number, any, undefined>'.
130130
!!! error TS2322: Types of property 'next' are incompatible.
131-
!!! error TS2322: Type '(...args: [] | [unknown]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<number, any>>'.
131+
!!! error TS2322: Type '(...args: [] | [unknown]) => Promise<IteratorResult<string, void>>' is not assignable to type '(...args: [] | [undefined]) => Promise<IteratorResult<number, any>>'.
132132
!!! error TS2322: Type 'Promise<IteratorResult<string, void>>' is not assignable to type 'Promise<IteratorResult<number, any>>'.
133133
yield* (async function * () { yield "a"; })();
134134
};
@@ -211,7 +211,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(
211211
~~~~~~~~~~~~~~~~
212212
!!! error TS2322: Type 'AsyncGenerator<number, any, undefined>' is not assignable to type 'Iterator<number, any, undefined>'.
213213
!!! error TS2322: Types of property 'next' are incompatible.
214-
!!! error TS2322: Type '(...args: [] | [PromiseLike<undefined>]) => Promise<IteratorResult<number, any>>' is not assignable to type '(...args: [] | [undefined]) => IteratorResult<number, any>'.
214+
!!! error TS2322: Type '(...args: [] | [undefined]) => Promise<IteratorResult<number, any>>' is not assignable to type '(...args: [] | [undefined]) => IteratorResult<number, any>'.
215215
!!! error TS2322: Type 'Promise<IteratorResult<number, any>>' is not assignable to type 'IteratorResult<number, any>'.
216216
!!! error TS2322: Property 'value' is missing in type 'Promise<IteratorResult<number, any>>' but required in type 'IteratorYieldResult<number>'.
217217
!!! related TS2728 /.ts/lib.es2015.iterable.d.ts:33:5: 'value' is declared here.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @target: esnext
2+
3+
// GH: https://github.com/microsoft/TypeScript/issues/33239
4+
class ConstantIterator<T> implements AsyncIterator<T, undefined, T | undefined> {
5+
constructor(private constant: T) {
6+
}
7+
async next(value?: T): Promise<IteratorResult<T>> {
8+
if (value != null) {
9+
throw new Error("ConstantIterator.prototype.next may not take any values");
10+
}
11+
return { value: this.constant, done: false };
12+
}
13+
}

0 commit comments

Comments
 (0)