From b800013c5068dbb68065f1f8ed6796a3674b1c19 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 10 Sep 2019 12:10:07 -0700 Subject: [PATCH] Fix incorrect parameter types for AsyncIterator next/return --- src/compiler/checker.ts | 6 ++-- src/lib/es2018.asyncgenerator.d.ts | 2 +- src/lib/es2018.asynciterable.d.ts | 2 +- .../reference/customAsyncIterator.js | 26 ++++++++++++++ .../reference/customAsyncIterator.symbols | 35 +++++++++++++++++++ .../reference/customAsyncIterator.types | 32 +++++++++++++++++ .../types.asyncGenerators.es2018.1.symbols | 9 +++++ .../types.asyncGenerators.es2018.1.types | 9 +++++ .../types.asyncGenerators.es2018.2.errors.txt | 20 +++++------ tests/cases/compiler/customAsyncIterator.ts | 13 +++++++ .../types.asyncGenerators.es2018.1.ts | 3 ++ 11 files changed, 143 insertions(+), 14 deletions(-) create mode 100644 tests/baselines/reference/customAsyncIterator.js create mode 100644 tests/baselines/reference/customAsyncIterator.symbols create mode 100644 tests/baselines/reference/customAsyncIterator.types create mode 100644 tests/cases/compiler/customAsyncIterator.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2328b7e3d68a4..0eaa4a3ccdf88 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28740,11 +28740,13 @@ namespace ts { let nextType: Type | undefined; if (methodName !== "throw") { const methodParameterType = methodParameterTypes ? getUnionType(methodParameterTypes) : unknownType; - const resolvedMethodParameterType = resolver.resolveIterationType(methodParameterType, errorNode) || anyType; if (methodName === "next") { - nextType = resolvedMethodParameterType; + // The value of `next(value)` is *not* awaited by async generators + nextType = methodParameterType; } else if (methodName === "return") { + // The value of `return(value)` *is* awaited by async generators + const resolvedMethodParameterType = resolver.resolveIterationType(methodParameterType, errorNode) || anyType; returnTypes = append(returnTypes, resolvedMethodParameterType); } } diff --git a/src/lib/es2018.asyncgenerator.d.ts b/src/lib/es2018.asyncgenerator.d.ts index 76275c92575c0..f6966264c8be4 100644 --- a/src/lib/es2018.asyncgenerator.d.ts +++ b/src/lib/es2018.asyncgenerator.d.ts @@ -2,7 +2,7 @@ interface AsyncGenerator extends AsyncIterator { // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. - next(...args: [] | [TNext | PromiseLike]): Promise>; + next(...args: [] | [TNext]): Promise>; return(value: TReturn | PromiseLike): Promise>; throw(e: any): Promise>; [Symbol.asyncIterator](): AsyncGenerator; diff --git a/src/lib/es2018.asynciterable.d.ts b/src/lib/es2018.asynciterable.d.ts index d5d83f31893bb..19a31c72ca4a9 100644 --- a/src/lib/es2018.asynciterable.d.ts +++ b/src/lib/es2018.asynciterable.d.ts @@ -11,7 +11,7 @@ interface SymbolConstructor { interface AsyncIterator { // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. - next(...args: [] | [TNext | PromiseLike]): Promise>; + next(...args: [] | [TNext]): Promise>; return?(value?: TReturn | PromiseLike): Promise>; throw?(e?: any): Promise>; } diff --git a/tests/baselines/reference/customAsyncIterator.js b/tests/baselines/reference/customAsyncIterator.js new file mode 100644 index 0000000000000..c0a86218e8d5c --- /dev/null +++ b/tests/baselines/reference/customAsyncIterator.js @@ -0,0 +1,26 @@ +//// [customAsyncIterator.ts] +// GH: https://github.com/microsoft/TypeScript/issues/33239 +class ConstantIterator implements AsyncIterator { + constructor(private constant: T) { + } + async next(value?: T): Promise> { + if (value != null) { + throw new Error("ConstantIterator.prototype.next may not take any values"); + } + return { value: this.constant, done: false }; + } +} + +//// [customAsyncIterator.js] +// GH: https://github.com/microsoft/TypeScript/issues/33239 +class ConstantIterator { + constructor(constant) { + this.constant = constant; + } + async next(value) { + if (value != null) { + throw new Error("ConstantIterator.prototype.next may not take any values"); + } + return { value: this.constant, done: false }; + } +} diff --git a/tests/baselines/reference/customAsyncIterator.symbols b/tests/baselines/reference/customAsyncIterator.symbols new file mode 100644 index 0000000000000..59c099ec40363 --- /dev/null +++ b/tests/baselines/reference/customAsyncIterator.symbols @@ -0,0 +1,35 @@ +=== tests/cases/compiler/customAsyncIterator.ts === +// GH: https://github.com/microsoft/TypeScript/issues/33239 +class ConstantIterator implements AsyncIterator { +>ConstantIterator : Symbol(ConstantIterator, Decl(customAsyncIterator.ts, 0, 0)) +>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23)) +>AsyncIterator : Symbol(AsyncIterator, Decl(lib.es2018.asynciterable.d.ts, --, --)) +>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23)) +>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23)) + + constructor(private constant: T) { +>constant : Symbol(ConstantIterator.constant, Decl(customAsyncIterator.ts, 2, 16)) +>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23)) + } + async next(value?: T): Promise> { +>next : Symbol(ConstantIterator.next, Decl(customAsyncIterator.ts, 3, 5)) +>value : Symbol(value, Decl(customAsyncIterator.ts, 4, 15)) +>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23)) +>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, --, --)) +>IteratorResult : Symbol(IteratorResult, Decl(lib.es2015.iterable.d.ts, --, --)) +>T : Symbol(T, Decl(customAsyncIterator.ts, 1, 23)) + + if (value != null) { +>value : Symbol(value, Decl(customAsyncIterator.ts, 4, 15)) + + throw new Error("ConstantIterator.prototype.next may not take any values"); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + } + return { value: this.constant, done: false }; +>value : Symbol(value, Decl(customAsyncIterator.ts, 8, 16)) +>this.constant : Symbol(ConstantIterator.constant, Decl(customAsyncIterator.ts, 2, 16)) +>this : Symbol(ConstantIterator, Decl(customAsyncIterator.ts, 0, 0)) +>constant : Symbol(ConstantIterator.constant, Decl(customAsyncIterator.ts, 2, 16)) +>done : Symbol(done, Decl(customAsyncIterator.ts, 8, 38)) + } +} diff --git a/tests/baselines/reference/customAsyncIterator.types b/tests/baselines/reference/customAsyncIterator.types new file mode 100644 index 0000000000000..63c67cfddd314 --- /dev/null +++ b/tests/baselines/reference/customAsyncIterator.types @@ -0,0 +1,32 @@ +=== tests/cases/compiler/customAsyncIterator.ts === +// GH: https://github.com/microsoft/TypeScript/issues/33239 +class ConstantIterator implements AsyncIterator { +>ConstantIterator : ConstantIterator + + constructor(private constant: T) { +>constant : T + } + async next(value?: T): Promise> { +>next : (value?: T) => Promise> +>value : T + + if (value != null) { +>value != null : boolean +>value : T +>null : null + + throw new Error("ConstantIterator.prototype.next may not take any values"); +>new Error("ConstantIterator.prototype.next may not take any values") : Error +>Error : ErrorConstructor +>"ConstantIterator.prototype.next may not take any values" : "ConstantIterator.prototype.next may not take any values" + } + return { value: this.constant, done: false }; +>{ value: this.constant, done: false } : { value: T; done: false; } +>value : T +>this.constant : T +>this : this +>constant : T +>done : false +>false : false + } +} diff --git a/tests/baselines/reference/types.asyncGenerators.es2018.1.symbols b/tests/baselines/reference/types.asyncGenerators.es2018.1.symbols index 3a0e33ce1b1d1..5cf681477e586 100644 --- a/tests/baselines/reference/types.asyncGenerators.es2018.1.symbols +++ b/tests/baselines/reference/types.asyncGenerators.es2018.1.symbols @@ -289,4 +289,13 @@ async function * awaitedType2() { >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, --, --)) >resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) } +async function * nextType1(): { next(...args: [] | [number | PromiseLike]): any } { +>nextType1 : Symbol(nextType1, Decl(types.asyncGenerators.es2018.1.ts, 122, 1)) +>next : Symbol(next, Decl(types.asyncGenerators.es2018.1.ts, 123, 31)) +>args : Symbol(args, Decl(types.asyncGenerators.es2018.1.ts, 123, 37)) +>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --)) + + const x = yield; // `number | PromiseLike` (should not await TNext) +>x : Symbol(x, Decl(types.asyncGenerators.es2018.1.ts, 124, 9)) +} diff --git a/tests/baselines/reference/types.asyncGenerators.es2018.1.types b/tests/baselines/reference/types.asyncGenerators.es2018.1.types index 54db9b890ecd7..3c6e51c56d220 100644 --- a/tests/baselines/reference/types.asyncGenerators.es2018.1.types +++ b/tests/baselines/reference/types.asyncGenerators.es2018.1.types @@ -430,4 +430,13 @@ async function * awaitedType2() { >resolve : { (value: T | PromiseLike): Promise; (): Promise; } >1 : 1 } +async function * nextType1(): { next(...args: [] | [number | PromiseLike]): any } { +>nextType1 : () => { next(...args: [] | [number | PromiseLike]): any; } +>next : (...args: [] | [number | PromiseLike]) => any +>args : [] | [number | PromiseLike] + + const x = yield; // `number | PromiseLike` (should not await TNext) +>x : number | PromiseLike +>yield : number | PromiseLike +} diff --git a/tests/baselines/reference/types.asyncGenerators.es2018.2.errors.txt b/tests/baselines/reference/types.asyncGenerators.es2018.2.errors.txt index d7ec011bcedc1..9f22060b9e936 100644 --- a/tests/baselines/reference/types.asyncGenerators.es2018.2.errors.txt +++ b/tests/baselines/reference/types.asyncGenerators.es2018.2.errors.txt @@ -3,7 +3,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts( tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(10,7): error TS2322: Type '() => AsyncGenerator' is not assignable to type '() => AsyncIterableIterator'. Type 'AsyncGenerator' is not assignable to type 'AsyncIterableIterator'. Types of property 'next' are incompatible. - Type '(...args: [] | [PromiseLike]) => Promise>' is not assignable to type '(...args: [] | [PromiseLike]) => Promise>'. + Type '(...args: [] | [undefined]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => Promise>'. Type 'Promise>' is not assignable to type 'Promise>'. Type 'IteratorResult' is not assignable to type 'IteratorResult'. Type 'IteratorYieldResult' is not assignable to type 'IteratorResult'. @@ -14,7 +14,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts( tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(16,7): error TS2322: Type '() => AsyncGenerator' is not assignable to type '() => AsyncIterableIterator'. Type 'AsyncGenerator' is not assignable to type 'AsyncIterableIterator'. Types of property 'next' are incompatible. - Type '(...args: [] | [unknown]) => Promise>' is not assignable to type '(...args: [] | [PromiseLike]) => Promise>'. + Type '(...args: [] | [unknown]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => Promise>'. Type 'Promise>' is not assignable to type 'Promise>'. tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(19,7): error TS2322: Type '() => AsyncGenerator' is not assignable to type '() => AsyncIterable'. Type 'AsyncGenerator' is not assignable to type 'AsyncIterable'. @@ -22,7 +22,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts( Type '() => AsyncGenerator' is not assignable to type '() => AsyncIterator'. Type 'AsyncGenerator' is not assignable to type 'AsyncIterator'. Types of property 'next' are incompatible. - Type '(...args: [] | [PromiseLike]) => Promise>' is not assignable to type '(...args: [] | [PromiseLike]) => Promise>'. + Type '(...args: [] | [undefined]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => Promise>'. Type 'Promise>' is not assignable to type 'Promise>'. tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(22,7): error TS2322: Type '() => AsyncGenerator' is not assignable to type '() => AsyncIterable'. Type 'AsyncGenerator' is not assignable to type 'AsyncIterable'. @@ -32,7 +32,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts( Type '() => AsyncGenerator' is not assignable to type '() => AsyncIterator'. Type 'AsyncGenerator' is not assignable to type 'AsyncIterator'. Types of property 'next' are incompatible. - Type '(...args: [] | [unknown]) => Promise>' is not assignable to type '(...args: [] | [PromiseLike]) => Promise>'. + Type '(...args: [] | [unknown]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => Promise>'. Type 'Promise>' is not assignable to type 'Promise>'. tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(28,7): error TS2322: Type '() => AsyncGenerator' is not assignable to type '() => AsyncIterator'. Type 'AsyncGenerator' is not assignable to type 'AsyncIterator'. @@ -53,7 +53,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts( tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(67,42): error TS2741: Property '[Symbol.iterator]' is missing in type 'AsyncGenerator' but required in type 'Iterable'. tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(70,42): error TS2322: Type 'AsyncGenerator' is not assignable to type 'Iterator'. Types of property 'next' are incompatible. - Type '(...args: [] | [PromiseLike]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => IteratorResult'. + Type '(...args: [] | [undefined]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => IteratorResult'. Type 'Promise>' is not assignable to type 'IteratorResult'. Property 'value' is missing in type 'Promise>' but required in type 'IteratorYieldResult'. 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( !!! error TS2322: Type '() => AsyncGenerator' is not assignable to type '() => AsyncIterableIterator'. !!! error TS2322: Type 'AsyncGenerator' is not assignable to type 'AsyncIterableIterator'. !!! error TS2322: Types of property 'next' are incompatible. -!!! error TS2322: Type '(...args: [] | [PromiseLike]) => Promise>' is not assignable to type '(...args: [] | [PromiseLike]) => Promise>'. +!!! error TS2322: Type '(...args: [] | [undefined]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => Promise>'. !!! error TS2322: Type 'Promise>' is not assignable to type 'Promise>'. !!! error TS2322: Type 'IteratorResult' is not assignable to type 'IteratorResult'. !!! error TS2322: Type 'IteratorYieldResult' is not assignable to type 'IteratorResult'. @@ -98,7 +98,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts( !!! error TS2322: Type '() => AsyncGenerator' is not assignable to type '() => AsyncIterableIterator'. !!! error TS2322: Type 'AsyncGenerator' is not assignable to type 'AsyncIterableIterator'. !!! error TS2322: Types of property 'next' are incompatible. -!!! error TS2322: Type '(...args: [] | [unknown]) => Promise>' is not assignable to type '(...args: [] | [PromiseLike]) => Promise>'. +!!! error TS2322: Type '(...args: [] | [unknown]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => Promise>'. !!! error TS2322: Type 'Promise>' is not assignable to type 'Promise>'. yield* (async function * () { yield "a"; })(); }; @@ -110,7 +110,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts( !!! error TS2322: Type '() => AsyncGenerator' is not assignable to type '() => AsyncIterator'. !!! error TS2322: Type 'AsyncGenerator' is not assignable to type 'AsyncIterator'. !!! error TS2322: Types of property 'next' are incompatible. -!!! error TS2322: Type '(...args: [] | [PromiseLike]) => Promise>' is not assignable to type '(...args: [] | [PromiseLike]) => Promise>'. +!!! error TS2322: Type '(...args: [] | [undefined]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => Promise>'. !!! error TS2322: Type 'Promise>' is not assignable to type 'Promise>'. yield "a"; }; @@ -128,7 +128,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts( !!! error TS2322: Type '() => AsyncGenerator' is not assignable to type '() => AsyncIterator'. !!! error TS2322: Type 'AsyncGenerator' is not assignable to type 'AsyncIterator'. !!! error TS2322: Types of property 'next' are incompatible. -!!! error TS2322: Type '(...args: [] | [unknown]) => Promise>' is not assignable to type '(...args: [] | [PromiseLike]) => Promise>'. +!!! error TS2322: Type '(...args: [] | [unknown]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => Promise>'. !!! error TS2322: Type 'Promise>' is not assignable to type 'Promise>'. yield* (async function * () { yield "a"; })(); }; @@ -211,7 +211,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts( ~~~~~~~~~~~~~~~~ !!! error TS2322: Type 'AsyncGenerator' is not assignable to type 'Iterator'. !!! error TS2322: Types of property 'next' are incompatible. -!!! error TS2322: Type '(...args: [] | [PromiseLike]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => IteratorResult'. +!!! error TS2322: Type '(...args: [] | [undefined]) => Promise>' is not assignable to type '(...args: [] | [undefined]) => IteratorResult'. !!! error TS2322: Type 'Promise>' is not assignable to type 'IteratorResult'. !!! error TS2322: Property 'value' is missing in type 'Promise>' but required in type 'IteratorYieldResult'. !!! related TS2728 /.ts/lib.es2015.iterable.d.ts:33:5: 'value' is declared here. diff --git a/tests/cases/compiler/customAsyncIterator.ts b/tests/cases/compiler/customAsyncIterator.ts new file mode 100644 index 0000000000000..dcd1140f6b5cd --- /dev/null +++ b/tests/cases/compiler/customAsyncIterator.ts @@ -0,0 +1,13 @@ +// @target: esnext + +// GH: https://github.com/microsoft/TypeScript/issues/33239 +class ConstantIterator implements AsyncIterator { + constructor(private constant: T) { + } + async next(value?: T): Promise> { + if (value != null) { + throw new Error("ConstantIterator.prototype.next may not take any values"); + } + return { value: this.constant, done: false }; + } +} \ No newline at end of file diff --git a/tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.1.ts b/tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.1.ts index f31f453ba5f15..6d39be9cef75e 100644 --- a/tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.1.ts +++ b/tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.1.ts @@ -124,3 +124,6 @@ async function * awaitedType1() { async function * awaitedType2() { const x = await Promise.resolve(1); } +async function * nextType1(): { next(...args: [] | [number | PromiseLike]): any } { + const x = yield; // `number | PromiseLike` (should not await TNext) +}