diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9d241c680b8..5baffcf7504 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33881,8 +33881,9 @@ namespace ts { if (iterationTypes === noIterationTypes) { if (errorNode) { reportTypeNotIterableError(errorNode, type, !!(use & IterationUse.AllowsAsyncIterablesFlag)); - errorNode = undefined; } + setCachedIterationTypes(type, cacheKey, noIterationTypes); + return undefined; } else { allIterationTypes = append(allIterationTypes, iterationTypes); @@ -34224,6 +34225,28 @@ namespace ts { return methodName === "next" ? anyIterationTypes : undefined; } + // If the method signature comes exclusively from the global iterator or generator type, + // create iteration types from its type arguments like `getIterationTypesOfIteratorFast` + // does (so as to remove `undefined` from the next and return types). We arrive here when + // a contextual type for a generator was not a direct reference to one of those global types, + // but looking up `methodType` referred to one of them (and nothing else). E.g., in + // `interface SpecialIterator extends Iterator {}`, `SpecialIterator` is not a + // reference to `Iterator`, but its `next` member derives exclusively from `Iterator`. + if (methodType?.symbol && methodSignatures.length === 1) { + const globalGeneratorType = resolver.getGlobalGeneratorType(/*reportErrors*/ false); + const globalIteratorType = resolver.getGlobalIteratorType(/*reportErrors*/ false); + const isGeneratorMethod = globalGeneratorType.symbol?.members?.get(methodName as __String) === methodType.symbol; + const isIteratorMethod = !isGeneratorMethod && globalIteratorType.symbol?.members?.get(methodName as __String) === methodType.symbol; + if (isGeneratorMethod || isIteratorMethod) { + const globalType = isGeneratorMethod ? globalGeneratorType : globalIteratorType; + const { mapper } = methodType as AnonymousType; + return createIterationTypes( + getMappedType(globalType.typeParameters![0], mapper!), + getMappedType(globalType.typeParameters![1], mapper!), + methodName === "next" ? getMappedType(globalType.typeParameters![2], mapper!) : undefined); + } + } + // Extract the first parameter and return type of each signature. let methodParameterTypes: Type[] | undefined; let methodReturnTypes: Type[] | undefined; diff --git a/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.errors.txt b/tests/baselines/reference/destructuringArrayBindingPatternAndAssignment4.errors.txt new file mode 100644 index 00000000000..6d344015ca7 --- /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 00000000000..8329982621f --- /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 00000000000..f1d6365814b --- /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 00000000000..20367ebf11f --- /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/generatorReturnTypeIndirectReferenceToGlobalType.js b/tests/baselines/reference/generatorReturnTypeIndirectReferenceToGlobalType.js new file mode 100644 index 00000000000..9ee5ae12d7e --- /dev/null +++ b/tests/baselines/reference/generatorReturnTypeIndirectReferenceToGlobalType.js @@ -0,0 +1,15 @@ +//// [generatorReturnTypeIndirectReferenceToGlobalType.ts] +interface I1 extends Iterator<0, 1, 2> {} + +function* f1(): I1 { + const a = yield 0; + return 1; +} + + +//// [generatorReturnTypeIndirectReferenceToGlobalType.js] +"use strict"; +function* f1() { + const a = yield 0; + return 1; +} diff --git a/tests/baselines/reference/generatorReturnTypeIndirectReferenceToGlobalType.symbols b/tests/baselines/reference/generatorReturnTypeIndirectReferenceToGlobalType.symbols new file mode 100644 index 00000000000..2b20b983f3f --- /dev/null +++ b/tests/baselines/reference/generatorReturnTypeIndirectReferenceToGlobalType.symbols @@ -0,0 +1,15 @@ +=== tests/cases/conformance/generators/generatorReturnTypeIndirectReferenceToGlobalType.ts === +interface I1 extends Iterator<0, 1, 2> {} +>I1 : Symbol(I1, Decl(generatorReturnTypeIndirectReferenceToGlobalType.ts, 0, 0)) +>Iterator : Symbol(Iterator, Decl(lib.es2015.iterable.d.ts, --, --)) + +function* f1(): I1 { +>f1 : Symbol(f1, Decl(generatorReturnTypeIndirectReferenceToGlobalType.ts, 0, 41)) +>I1 : Symbol(I1, Decl(generatorReturnTypeIndirectReferenceToGlobalType.ts, 0, 0)) + + const a = yield 0; +>a : Symbol(a, Decl(generatorReturnTypeIndirectReferenceToGlobalType.ts, 3, 7)) + + return 1; +} + diff --git a/tests/baselines/reference/generatorReturnTypeIndirectReferenceToGlobalType.types b/tests/baselines/reference/generatorReturnTypeIndirectReferenceToGlobalType.types new file mode 100644 index 00000000000..05955b31526 --- /dev/null +++ b/tests/baselines/reference/generatorReturnTypeIndirectReferenceToGlobalType.types @@ -0,0 +1,15 @@ +=== tests/cases/conformance/generators/generatorReturnTypeIndirectReferenceToGlobalType.ts === +interface I1 extends Iterator<0, 1, 2> {} + +function* f1(): I1 { +>f1 : () => I1 + + const a = yield 0; +>a : 2 +>yield 0 : 2 +>0 : 0 + + return 1; +>1 : 1 +} + diff --git a/tests/baselines/reference/generatorYieldContextualType.types b/tests/baselines/reference/generatorYieldContextualType.types index a90bbd040a1..13a2b85267d 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/baselines/reference/types.asyncGenerators.es2018.2.errors.txt b/tests/baselines/reference/types.asyncGenerators.es2018.2.errors.txt index 6c76d41809a..6f3a7581e64 100644 --- a/tests/baselines/reference/types.asyncGenerators.es2018.2.errors.txt +++ b/tests/baselines/reference/types.asyncGenerators.es2018.2.errors.txt @@ -44,7 +44,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts( tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts(70,42): error TS2322: Type 'AsyncGenerator' is not assignable to type 'Iterator'. The types returned by 'next(...)' are incompatible between these types. Type 'Promise>' is not assignable to type 'IteratorResult'. - Property 'value' is missing in type 'Promise>' but required in type 'IteratorYieldResult'. + Type 'Promise>' is missing the following properties from type 'IteratorReturnResult': done, value 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. @@ -191,8 +191,7 @@ tests/cases/conformance/types/asyncGenerators/types.asyncGenerators.es2018.2.ts( !!! error TS2322: Type 'AsyncGenerator' is not assignable to type 'Iterator'. !!! error TS2322: The types returned by 'next(...)' are incompatible between these types. !!! 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. +!!! error TS2322: Type 'Promise>' is missing the following properties from type 'IteratorReturnResult': done, value yield 1; } async function * yieldStar() { diff --git a/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment4.ts b/tests/cases/conformance/es6/destructuring/destructuringArrayBindingPatternAndAssignment4.ts new file mode 100644 index 00000000000..bc5fc9a9157 --- /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 diff --git a/tests/cases/conformance/generators/generatorReturnTypeIndirectReferenceToGlobalType.ts b/tests/cases/conformance/generators/generatorReturnTypeIndirectReferenceToGlobalType.ts new file mode 100644 index 00000000000..27afdc918f9 --- /dev/null +++ b/tests/cases/conformance/generators/generatorReturnTypeIndirectReferenceToGlobalType.ts @@ -0,0 +1,9 @@ +// @strict: true +// @target: esnext + +interface I1 extends Iterator<0, 1, 2> {} + +function* f1(): I1 { + const a = yield 0; + return 1; +}