From b632ef68f1a156a2e7f505283c9f6e2a06e32cf0 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 27 Jul 2020 09:34:12 -0700 Subject: [PATCH 1/2] WIP --- src/compiler/checker.ts | 2 ++ ...rayBindingPatternAndAssignment4.errors.txt | 12 ++++++++ ...turingArrayBindingPatternAndAssignment4.js | 28 +++++++++++++++++++ ...gArrayBindingPatternAndAssignment4.symbols | 11 ++++++++ ...ingArrayBindingPatternAndAssignment4.types | 12 ++++++++ .../generatorYieldContextualType.types | 6 ++-- ...turingArrayBindingPatternAndAssignment4.ts | 9 ++++++ 7 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.errors.txt create mode 100644 tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.js create mode 100644 tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.symbols create mode 100644 tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.types create mode 100644 tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment4.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 69efd925e6e72..ec5e00568ff10 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33436,6 +33436,8 @@ namespace ts { reportTypeNotIterableError(errorNode, type, !!(use & IterationUse.AllowsAsyncIterablesFlag)); errorNode = undefined; } + setCachedIterationTypes(type, cacheKey, noIterationTypes); + return undefined; } else { allIterationTypes = append(allIterationTypes, iterationTypes); diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.errors.txt b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.errors.txt new file mode 100644 index 0000000000000..6d344015ca71b --- /dev/null +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.errors.txt @@ -0,0 +1,12 @@ +tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment4.ts(5,7): error TS2548: Type 'number[] | null' is not an array type or does not have a '[Symbol.iterator]()' method that returns an iterator. + + +==== tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment4.ts (1 errors) ==== + // #35497 + + + declare const data: number[] | null; + const [value] = data; // Error + ~~~~~~~ +!!! error TS2548: Type 'number[] | null' is not an array type or does not have a '[Symbol.iterator]()' method that returns an iterator. + \ No newline at end of file diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.js b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.js new file mode 100644 index 0000000000000..8329982621f4f --- /dev/null +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.js @@ -0,0 +1,28 @@ +//// [destructuringArrayBindingPatternAndAssignment4.ts] +// #35497 + + +declare const data: number[] | null; +const [value] = data; // Error + + +//// [destructuringArrayBindingPatternAndAssignment4.js] +"use strict"; +// #35497 +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var _a = __read(data, 1), value = _a[0]; // Error diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.symbols b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.symbols new file mode 100644 index 0000000000000..f1d6365814b83 --- /dev/null +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.symbols @@ -0,0 +1,11 @@ +=== tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment4.ts === +// #35497 + + +declare const data: number[] | null; +>data : Symbol(data, Decl(destructuringArrayBindingPatternAndAssignment4.ts, 3, 13)) + +const [value] = data; // Error +>value : Symbol(value, Decl(destructuringArrayBindingPatternAndAssignment4.ts, 4, 7)) +>data : Symbol(data, Decl(destructuringArrayBindingPatternAndAssignment4.ts, 3, 13)) + diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.types b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.types new file mode 100644 index 0000000000000..20367ebf11feb --- /dev/null +++ b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.types @@ -0,0 +1,12 @@ +=== tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment4.ts === +// #35497 + + +declare const data: number[] | null; +>data : number[] | null +>null : null + +const [value] = data; // Error +>value : any +>data : number[] | null + diff --git a/tests/baselines/reference/generatorYieldContextualType.types b/tests/baselines/reference/generatorYieldContextualType.types index a90bbd040a1cb..13a2b85267d14 100644 --- a/tests/baselines/reference/generatorYieldContextualType.types +++ b/tests/baselines/reference/generatorYieldContextualType.types @@ -25,11 +25,11 @@ declare function f2(gen: () => Generator | AsyncGenerator(async function* () { >f2<0, 0, 1>(async function* () { const a = yield 0; return 0;}) : void >f2 : (gen: () => Generator | AsyncGenerator) => void ->async function* () { const a = yield 0; return 0;} : () => AsyncGenerator<0, 0, 1> +>async function* () { const a = yield 0; return 0;} : () => AsyncGenerator<0, 0, 1 | undefined> const a = yield 0; ->a : 1 ->yield 0 : 1 +>a : 1 | undefined +>yield 0 : 1 | undefined >0 : 0 return 0; diff --git a/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment4.ts b/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment4.ts new file mode 100644 index 0000000000000..bc5fc9a915732 --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment4.ts @@ -0,0 +1,9 @@ +// #35497 + +// @target: es5 +// @downlevelIteration: true +// @lib: es6 +// @strict: true + +declare const data: number[] | null; +const [value] = data; // Error From a2a8f6e0a9977d4d9ab90c1993d19e4edbbfa5fb Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 27 Jul 2020 11:19:07 -0700 Subject: [PATCH 2/2] Add contextual type for generator return type --- src/compiler/checker.ts | 25 ++++++--- .../generatorReturnContextualType.errors.txt | 29 ++++++++++ .../generatorReturnContextualType.js | 37 +++++++++++++ .../generatorReturnContextualType.symbols | 49 +++++++++++++++++ .../generatorReturnContextualType.types | 55 +++++++++++++++++++ .../generatorReturnContextualType.ts | 21 +++++++ 6 files changed, 207 insertions(+), 9 deletions(-) create mode 100644 tests/baselines/reference/generatorReturnContextualType.errors.txt create mode 100644 tests/baselines/reference/generatorReturnContextualType.js create mode 100644 tests/baselines/reference/generatorReturnContextualType.symbols create mode 100644 tests/baselines/reference/generatorReturnContextualType.types create mode 100644 tests/cases/conformance/generators/generatorReturnContextualType.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ec5e00568ff10..0049e6b9bbd9b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22974,18 +22974,25 @@ namespace ts { function getContextualTypeForReturnExpression(node: Expression): Type | undefined { const func = getContainingFunction(node); if (func) { - const functionFlags = getFunctionFlags(func); - if (functionFlags & FunctionFlags.Generator) { // AsyncGenerator function or Generator function - return undefined; - } - - const contextualReturnType = getContextualReturnType(func); + let contextualReturnType = getContextualReturnType(func); if (contextualReturnType) { - if (functionFlags & FunctionFlags.Async) { // Async function - const contextualAwaitedType = mapType(contextualReturnType, getAwaitedTypeOfPromise); + const functionFlags = getFunctionFlags(func); + if (functionFlags & FunctionFlags.Generator) { // Generator or AsyncGenerator function + const use = functionFlags & FunctionFlags.Async ? IterationUse.AsyncGeneratorReturnType : IterationUse.GeneratorReturnType; + const iterationTypes = getIterationTypesOfIterable(contextualReturnType, use, /*errorNode*/ undefined); + if (!iterationTypes) { + return undefined; + } + contextualReturnType = iterationTypes.returnType; + // falls through to unwrap Promise for AsyncGenerators + } + + if (functionFlags & FunctionFlags.Async) { // Async function or AsyncGenerator function + const contextualAwaitedType = mapType(contextualReturnType, getAwaitedType); return contextualAwaitedType && getUnionType([contextualAwaitedType, createPromiseLikeType(contextualAwaitedType)]); } - return contextualReturnType; // Regular function + + return contextualReturnType; // Regular function or Generator function } } return undefined; diff --git a/tests/baselines/reference/generatorReturnContextualType.errors.txt b/tests/baselines/reference/generatorReturnContextualType.errors.txt new file mode 100644 index 0000000000000..48724292ea99c --- /dev/null +++ b/tests/baselines/reference/generatorReturnContextualType.errors.txt @@ -0,0 +1,29 @@ +tests/cases/conformance/generators/generatorReturnContextualType.ts(17,3): error TS2322: Type '{ x: string; }' is not assignable to type '{ x: "x"; }'. + Types of property 'x' are incompatible. + Type 'string' is not assignable to type '"x"'. + + +==== tests/cases/conformance/generators/generatorReturnContextualType.ts (1 errors) ==== + // #35995 + + function* f1(): Generator { + return { x: 'x' }; + } + + async function* f2(): AsyncGenerator { + return { x: 'x' }; + } + + async function* f3(): AsyncGenerator { + return Promise.resolve({ x: 'x' }); + } + + async function* f4(): AsyncGenerator { + const ret = { x: 'x' }; + return Promise.resolve(ret); // Error + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ x: string; }' is not assignable to type '{ x: "x"; }'. +!!! error TS2322: Types of property 'x' are incompatible. +!!! error TS2322: Type 'string' is not assignable to type '"x"'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/generatorReturnContextualType.js b/tests/baselines/reference/generatorReturnContextualType.js new file mode 100644 index 0000000000000..5c613ee6f4998 --- /dev/null +++ b/tests/baselines/reference/generatorReturnContextualType.js @@ -0,0 +1,37 @@ +//// [generatorReturnContextualType.ts] +// #35995 + +function* f1(): Generator { + return { x: 'x' }; +} + +async function* f2(): AsyncGenerator { + return { x: 'x' }; +} + +async function* f3(): AsyncGenerator { + return Promise.resolve({ x: 'x' }); +} + +async function* f4(): AsyncGenerator { + const ret = { x: 'x' }; + return Promise.resolve(ret); // Error +} + + +//// [generatorReturnContextualType.js] +"use strict"; +// #35995 +function* f1() { + return { x: 'x' }; +} +async function* f2() { + return { x: 'x' }; +} +async function* f3() { + return Promise.resolve({ x: 'x' }); +} +async function* f4() { + const ret = { x: 'x' }; + return Promise.resolve(ret); // Error +} diff --git a/tests/baselines/reference/generatorReturnContextualType.symbols b/tests/baselines/reference/generatorReturnContextualType.symbols new file mode 100644 index 0000000000000..15dce2efcd67b --- /dev/null +++ b/tests/baselines/reference/generatorReturnContextualType.symbols @@ -0,0 +1,49 @@ +=== tests/cases/conformance/generators/generatorReturnContextualType.ts === +// #35995 + +function* f1(): Generator { +>f1 : Symbol(f1, Decl(generatorReturnContextualType.ts, 0, 0)) +>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --)) +>x : Symbol(x, Decl(generatorReturnContextualType.ts, 2, 32)) + + return { x: 'x' }; +>x : Symbol(x, Decl(generatorReturnContextualType.ts, 3, 10)) +} + +async function* f2(): AsyncGenerator { +>f2 : Symbol(f2, Decl(generatorReturnContextualType.ts, 4, 1)) +>AsyncGenerator : Symbol(AsyncGenerator, Decl(lib.es2018.asyncgenerator.d.ts, --, --)) +>x : Symbol(x, Decl(generatorReturnContextualType.ts, 6, 43)) + + return { x: 'x' }; +>x : Symbol(x, Decl(generatorReturnContextualType.ts, 7, 10)) +} + +async function* f3(): AsyncGenerator { +>f3 : Symbol(f3, Decl(generatorReturnContextualType.ts, 8, 1)) +>AsyncGenerator : Symbol(AsyncGenerator, Decl(lib.es2018.asyncgenerator.d.ts, --, --)) +>x : Symbol(x, Decl(generatorReturnContextualType.ts, 10, 43)) + + return Promise.resolve({ x: 'x' }); +>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>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, --, --)) +>x : Symbol(x, Decl(generatorReturnContextualType.ts, 11, 26)) +} + +async function* f4(): AsyncGenerator { +>f4 : Symbol(f4, Decl(generatorReturnContextualType.ts, 12, 1)) +>AsyncGenerator : Symbol(AsyncGenerator, Decl(lib.es2018.asyncgenerator.d.ts, --, --)) +>x : Symbol(x, Decl(generatorReturnContextualType.ts, 14, 43)) + + const ret = { x: 'x' }; +>ret : Symbol(ret, Decl(generatorReturnContextualType.ts, 15, 7)) +>x : Symbol(x, Decl(generatorReturnContextualType.ts, 15, 15)) + + return Promise.resolve(ret); // Error +>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>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, --, --)) +>ret : Symbol(ret, Decl(generatorReturnContextualType.ts, 15, 7)) +} + diff --git a/tests/baselines/reference/generatorReturnContextualType.types b/tests/baselines/reference/generatorReturnContextualType.types new file mode 100644 index 0000000000000..7706c2f231a35 --- /dev/null +++ b/tests/baselines/reference/generatorReturnContextualType.types @@ -0,0 +1,55 @@ +=== tests/cases/conformance/generators/generatorReturnContextualType.ts === +// #35995 + +function* f1(): Generator { +>f1 : () => Generator +>x : "x" + + return { x: 'x' }; +>{ x: 'x' } : { x: "x"; } +>x : "x" +>'x' : "x" +} + +async function* f2(): AsyncGenerator { +>f2 : () => AsyncGenerator +>x : "x" + + return { x: 'x' }; +>{ x: 'x' } : { x: "x"; } +>x : "x" +>'x' : "x" +} + +async function* f3(): AsyncGenerator { +>f3 : () => AsyncGenerator +>x : "x" + + return Promise.resolve({ x: 'x' }); +>Promise.resolve({ x: 'x' }) : Promise<{ x: "x"; }> +>Promise.resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>Promise : PromiseConstructor +>resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>{ x: 'x' } : { x: "x"; } +>x : "x" +>'x' : "x" +} + +async function* f4(): AsyncGenerator { +>f4 : () => AsyncGenerator +>x : "x" + + const ret = { x: 'x' }; +>ret : { x: string; } +>{ x: 'x' } : { x: string; } +>x : string +>'x' : "x" + + return Promise.resolve(ret); // Error +>Promise.resolve(ret) : Promise<{ x: string; }> +>Promise.resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>Promise : PromiseConstructor +>resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>ret : { x: string; } +} + diff --git a/tests/cases/conformance/generators/generatorReturnContextualType.ts b/tests/cases/conformance/generators/generatorReturnContextualType.ts new file mode 100644 index 0000000000000..71090a2c9924d --- /dev/null +++ b/tests/cases/conformance/generators/generatorReturnContextualType.ts @@ -0,0 +1,21 @@ +// @target: esnext +// @strict: true + +// #35995 + +function* f1(): Generator { + return { x: 'x' }; +} + +async function* f2(): AsyncGenerator { + return { x: 'x' }; +} + +async function* f3(): AsyncGenerator { + return Promise.resolve({ x: 'x' }); +} + +async function* f4(): AsyncGenerator { + const ret = { x: 'x' }; + return Promise.resolve(ret); // Error +}