A union including non-iterable types is not iterable (#40350)

* WIP

* If method type derives solely from the global iterator or generator type, use its type arguments

* Add test for problem fixed as side effect
This commit is contained in:
Andrew Branch
2020-09-11 09:31:22 -07:00
committed by GitHub
parent 4a3b195772
commit 083129f005
12 changed files with 155 additions and 7 deletions

View File

@@ -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<number> {}`, `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;

View File

@@ -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.

View File

@@ -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

View File

@@ -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))

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -25,11 +25,11 @@ declare function f2<T, R, S>(gen: () => Generator<R, T, S> | AsyncGenerator<R, T
f2<0, 0, 1>(async function* () {
>f2<0, 0, 1>(async function* () { const a = yield 0; return 0;}) : void
>f2 : <T, R, S>(gen: () => Generator<R, T, S> | AsyncGenerator<R, T, S>) => 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;

View File

@@ -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<number, any, undefined>' is not assignable to type 'Iterator<number, any, undefined>'.
The types returned by 'next(...)' are incompatible between these types.
Type 'Promise<IteratorResult<number, any>>' is not assignable to type 'IteratorResult<number, any>'.
Property 'value' is missing in type 'Promise<IteratorResult<number, any>>' but required in type 'IteratorYieldResult<number>'.
Type 'Promise<IteratorResult<number, any>>' is missing the following properties from type 'IteratorReturnResult<any>': 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<number, any, undefined>' is not assignable to type 'Iterator<number, any, undefined>'.
!!! error TS2322: The types returned by 'next(...)' are incompatible between these types.
!!! error TS2322: Type 'Promise<IteratorResult<number, any>>' is not assignable to type 'IteratorResult<number, any>'.
!!! error TS2322: Property 'value' is missing in type 'Promise<IteratorResult<number, any>>' but required in type 'IteratorYieldResult<number>'.
!!! related TS2728 /.ts/lib.es2015.iterable.d.ts:33:5: 'value' is declared here.
!!! error TS2322: Type 'Promise<IteratorResult<number, any>>' is missing the following properties from type 'IteratorReturnResult<any>': done, value
yield 1;
}
async function * yieldStar() {

View File

@@ -0,0 +1,9 @@
// #35497
// @target: es5
// @downlevelIteration: true
// @lib: es6
// @strict: true
declare const data: number[] | null;
const [value] = data; // Error

View File

@@ -0,0 +1,9 @@
// @strict: true
// @target: esnext
interface I1 extends Iterator<0, 1, 2> {}
function* f1(): I1 {
const a = yield 0;
return 1;
}