Skip to content

Commit 17762c4

Browse files
authored
Fall back to (Async)IterableIterator if (Async)Generator not found (#32303)
1 parent 0038b0b commit 17762c4

22 files changed

+209
-33
lines changed

src/compiler/checker.ts

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ namespace ts {
4242
iterableCacheKey: "iterationTypesOfAsyncIterable" | "iterationTypesOfIterable";
4343
iteratorCacheKey: "iterationTypesOfAsyncIterator" | "iterationTypesOfIterator";
4444
iteratorSymbolName: "asyncIterator" | "iterator";
45-
getGlobalIteratorType: (reportErrors: boolean) => Type;
46-
getGlobalIterableType: (reportErrors: boolean) => Type;
47-
getGlobalIterableIteratorType: (reportErrors: boolean) => Type;
48-
getGlobalGeneratorType: (reportErrors: boolean) => Type;
45+
getGlobalIteratorType: (reportErrors: boolean) => GenericType;
46+
getGlobalIterableType: (reportErrors: boolean) => GenericType;
47+
getGlobalIterableIteratorType: (reportErrors: boolean) => GenericType;
48+
getGlobalGeneratorType: (reportErrors: boolean) => GenericType;
4949
resolveIterationType: (type: Type, errorNode: Node | undefined) => Type | undefined;
5050
mustHaveANextMethodDiagnostic: DiagnosticMessage;
5151
mustBeAMethodDiagnostic: DiagnosticMessage;
@@ -9531,24 +9531,10 @@ namespace ts {
95319531
return createTypeFromGenericGlobalType(getGlobalTypedPropertyDescriptorType(), [propertyType]);
95329532
}
95339533

9534-
function createAsyncGeneratorType(yieldType: Type, returnType: Type, nextType: Type) {
9535-
const globalAsyncGeneratorType = getGlobalAsyncGeneratorType(/*reportErrors*/ true);
9536-
if (globalAsyncGeneratorType !== emptyGenericType) {
9537-
yieldType = getAwaitedType(yieldType) || unknownType;
9538-
returnType = getAwaitedType(returnType) || unknownType;
9539-
nextType = getAwaitedType(nextType) || unknownType;
9540-
}
9541-
return createTypeFromGenericGlobalType(globalAsyncGeneratorType, [yieldType, returnType, nextType]);
9542-
}
9543-
95449534
function createIterableType(iteratedType: Type): Type {
95459535
return createTypeFromGenericGlobalType(getGlobalIterableType(/*reportErrors*/ true), [iteratedType]);
95469536
}
95479537

9548-
function createGeneratorType(yieldType: Type, returnType: Type, nextType: Type) {
9549-
return createTypeFromGenericGlobalType(getGlobalGeneratorType(/*reportErrors*/ true), [yieldType, returnType, nextType]);
9550-
}
9551-
95529538
function createArrayType(elementType: Type, readonly?: boolean): ObjectType {
95539539
return createTypeFromGenericGlobalType(readonly ? globalReadonlyArrayType : globalArrayType, [elementType]);
95549540
}
@@ -23401,9 +23387,36 @@ namespace ts {
2340123387
}
2340223388

2340323389
function createGeneratorReturnType(yieldType: Type, returnType: Type, nextType: Type, isAsyncGenerator: boolean) {
23404-
return isAsyncGenerator
23405-
? createAsyncGeneratorType(yieldType, returnType, nextType)
23406-
: createGeneratorType(yieldType, returnType, nextType);
23390+
const resolver = isAsyncGenerator ? asyncIterationTypesResolver : syncIterationTypesResolver;
23391+
const globalGeneratorType = resolver.getGlobalGeneratorType(/*reportErrors*/ false);
23392+
yieldType = resolver.resolveIterationType(yieldType, /*errorNode*/ undefined) || unknownType;
23393+
returnType = resolver.resolveIterationType(returnType, /*errorNode*/ undefined) || unknownType;
23394+
nextType = resolver.resolveIterationType(nextType, /*errorNode*/ undefined) || unknownType;
23395+
if (globalGeneratorType === emptyGenericType) {
23396+
// Fall back to the global IterableIterator if returnType is assignable to the expected return iteration
23397+
// type of IterableIterator, and the expected next iteration type of IterableIterator is assignable to
23398+
// nextType.
23399+
const globalType = resolver.getGlobalIterableIteratorType(/*reportErrors*/ false);
23400+
const iterationTypes = globalType !== emptyGenericType ? getIterationTypesOfGlobalIterableType(globalType, resolver) : undefined;
23401+
const iterableIteratorReturnType = iterationTypes ? iterationTypes.returnType : anyType;
23402+
const iterableIteratorNextType = iterationTypes ? iterationTypes.nextType : undefinedType;
23403+
if (isTypeAssignableTo(returnType, iterableIteratorReturnType) &&
23404+
isTypeAssignableTo(iterableIteratorNextType, nextType)) {
23405+
if (globalType !== emptyGenericType) {
23406+
return createTypeFromGenericGlobalType(globalType, [yieldType]);
23407+
}
23408+
23409+
// The global IterableIterator type doesn't exist, so report an error
23410+
resolver.getGlobalIterableIteratorType(/*reportErrors*/ true);
23411+
return emptyObjectType;
23412+
}
23413+
23414+
// The global Generator type doesn't exist, so report an error
23415+
resolver.getGlobalGeneratorType(/*reportErrors*/ true);
23416+
return emptyObjectType;
23417+
}
23418+
23419+
return createTypeFromGenericGlobalType(globalGeneratorType, [yieldType, returnType, nextType]);
2340723420
}
2340823421

2340923422
function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, checkMode: CheckMode | undefined) {
@@ -28240,6 +28253,13 @@ namespace ts {
2824028253
return (type as IterableOrIteratorType)[resolver.iterableCacheKey];
2824128254
}
2824228255

28256+
function getIterationTypesOfGlobalIterableType(globalType: Type, resolver: IterationTypesResolver) {
28257+
const globalIterationTypes =
28258+
getIterationTypesOfIterableCached(globalType, resolver) ||
28259+
getIterationTypesOfIterableSlow(globalType, resolver, /*errorNode*/ undefined);
28260+
return globalIterationTypes === noIterationTypes ? defaultIterationTypes : globalIterationTypes;
28261+
}
28262+
2824328263
/**
2824428264
* Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like
2824528265
* type from from common heuristics.
@@ -28265,10 +28285,7 @@ namespace ts {
2826528285
// iteration types of their `[Symbol.iterator]()` method. The same is true for their async cousins.
2826628286
// While we define these as `any` and `undefined` in our libs by default, a custom lib *could* use
2826728287
// different definitions.
28268-
const globalIterationTypes =
28269-
getIterationTypesOfIterableCached(globalType, resolver) ||
28270-
getIterationTypesOfIterableSlow(globalType, resolver, /*errorNode*/ undefined);
28271-
const { returnType, nextType } = globalIterationTypes === noIterationTypes ? defaultIterationTypes : globalIterationTypes;
28288+
const { returnType, nextType } = getIterationTypesOfGlobalIterableType(globalType, resolver);
2827228289
return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = createIterationTypes(yieldType, returnType, nextType);
2827328290
}
2827428291

tests/baselines/reference/castOfYield.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error TS2318: Cannot find global type 'Generator'.
1+
error TS2318: Cannot find global type 'IterableIterator'.
22
tests/cases/compiler/castOfYield.ts(4,14): error TS1109: Expression expected.
33

44

5-
!!! error TS2318: Cannot find global type 'Generator'.
5+
!!! error TS2318: Cannot find global type 'IterableIterator'.
66
==== tests/cases/compiler/castOfYield.ts (1 errors) ====
77
function* f() {
88
<number> (yield 0);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
=== tests/cases/conformance/generators/generatorReturnTypeFallback.1.ts ===
2+
// Allow generators to fallback to IterableIterator if they do not need a type for the sent value while in strictNullChecks mode.
3+
function* f() {
4+
>f : Symbol(f, Decl(generatorReturnTypeFallback.1.ts, 0, 0))
5+
6+
yield 1;
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/conformance/generators/generatorReturnTypeFallback.1.ts ===
2+
// Allow generators to fallback to IterableIterator if they do not need a type for the sent value while in strictNullChecks mode.
3+
function* f() {
4+
>f : () => IterableIterator<number>
5+
6+
yield 1;
7+
>yield 1 : any
8+
>1 : 1
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error TS2318: Cannot find global type 'IterableIterator'.
2+
3+
4+
!!! error TS2318: Cannot find global type 'IterableIterator'.
5+
==== tests/cases/conformance/generators/generatorReturnTypeFallback.2.ts (0 errors) ====
6+
// Allow generators to fallback to IterableIterator if they do not need a type for the sent value while in strictNullChecks mode.
7+
// Report an error if IterableIterator cannot be found.
8+
function* f() {
9+
yield 1;
10+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
=== tests/cases/conformance/generators/generatorReturnTypeFallback.2.ts ===
2+
// Allow generators to fallback to IterableIterator if they do not need a type for the sent value while in strictNullChecks mode.
3+
// Report an error if IterableIterator cannot be found.
4+
function* f() {
5+
>f : Symbol(f, Decl(generatorReturnTypeFallback.2.ts, 0, 0))
6+
7+
yield 1;
8+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== tests/cases/conformance/generators/generatorReturnTypeFallback.2.ts ===
2+
// Allow generators to fallback to IterableIterator if they do not need a type for the sent value while in strictNullChecks mode.
3+
// Report an error if IterableIterator cannot be found.
4+
function* f() {
5+
>f : () => {}
6+
7+
yield 1;
8+
>yield 1 : any
9+
>1 : 1
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error TS2318: Cannot find global type 'Generator'.
2+
3+
4+
!!! error TS2318: Cannot find global type 'Generator'.
5+
==== tests/cases/conformance/generators/generatorReturnTypeFallback.3.ts (0 errors) ====
6+
// Do not allow generators to fallback to IterableIterator while in strictNullChecks mode if they need a type for the sent value.
7+
// NOTE: In non-strictNullChecks mode, `undefined` (the default sent value) is assignable to everything.
8+
function* f() {
9+
const x: string = yield 1;
10+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/conformance/generators/generatorReturnTypeFallback.3.ts ===
2+
// Do not allow generators to fallback to IterableIterator while in strictNullChecks mode if they need a type for the sent value.
3+
// NOTE: In non-strictNullChecks mode, `undefined` (the default sent value) is assignable to everything.
4+
function* f() {
5+
>f : Symbol(f, Decl(generatorReturnTypeFallback.3.ts, 0, 0))
6+
7+
const x: string = yield 1;
8+
>x : Symbol(x, Decl(generatorReturnTypeFallback.3.ts, 3, 9))
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/conformance/generators/generatorReturnTypeFallback.3.ts ===
2+
// Do not allow generators to fallback to IterableIterator while in strictNullChecks mode if they need a type for the sent value.
3+
// NOTE: In non-strictNullChecks mode, `undefined` (the default sent value) is assignable to everything.
4+
function* f() {
5+
>f : () => {}
6+
7+
const x: string = yield 1;
8+
>x : string
9+
>yield 1 : any
10+
>1 : 1
11+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/conformance/generators/generatorReturnTypeFallback.4.ts ===
2+
// Allow generators to fallback to IterableIterator if they are not in strictNullChecks mode
3+
// NOTE: In non-strictNullChecks mode, `undefined` (the default sent value) is assignable to everything.
4+
function* f() {
5+
>f : Symbol(f, Decl(generatorReturnTypeFallback.4.ts, 0, 0))
6+
7+
const x: string = yield 1;
8+
>x : Symbol(x, Decl(generatorReturnTypeFallback.4.ts, 3, 9))
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/conformance/generators/generatorReturnTypeFallback.4.ts ===
2+
// Allow generators to fallback to IterableIterator if they are not in strictNullChecks mode
3+
// NOTE: In non-strictNullChecks mode, `undefined` (the default sent value) is assignable to everything.
4+
function* f() {
5+
>f : () => IterableIterator<number>
6+
7+
const x: string = yield 1;
8+
>x : string
9+
>yield 1 : any
10+
>1 : 1
11+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
=== tests/cases/conformance/generators/generatorReturnTypeFallback.5.ts ===
2+
// Allow generators to fallback to IterableIterator if they do not need a type for the sent value while in strictNullChecks mode.
3+
function* f(): IterableIterator<number> {
4+
>f : Symbol(f, Decl(generatorReturnTypeFallback.5.ts, 0, 0))
5+
>IterableIterator : Symbol(IterableIterator, Decl(lib.es2015.iterable.d.ts, --, --))
6+
7+
yield 1;
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/conformance/generators/generatorReturnTypeFallback.5.ts ===
2+
// Allow generators to fallback to IterableIterator if they do not need a type for the sent value while in strictNullChecks mode.
3+
function* f(): IterableIterator<number> {
4+
>f : () => IterableIterator<number>
5+
6+
yield 1;
7+
>yield 1 : undefined
8+
>1 : 1
9+
}

tests/baselines/reference/spreadOfParamsFromGeneratorMakesRequiredParams.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error TS2318: Cannot find global type 'Generator'.
1+
error TS2318: Cannot find global type 'IterableIterator'.
22
tests/cases/compiler/spreadOfParamsFromGeneratorMakesRequiredParams.ts(6,1): error TS2554: Expected 2 arguments, but got 1.
33

44

5-
!!! error TS2318: Cannot find global type 'Generator'.
5+
!!! error TS2318: Cannot find global type 'IterableIterator'.
66
==== tests/cases/compiler/spreadOfParamsFromGeneratorMakesRequiredParams.ts (1 errors) ====
77
declare function call<Fn extends (...args: any[]) => any>(
88
fn: Fn,

tests/baselines/reference/templateStringWithEmbeddedYieldKeyword.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error TS2318: Cannot find global type 'Generator'.
1+
error TS2318: Cannot find global type 'IterableIterator'.
22
tests/cases/conformance/es6/templates/templateStringWithEmbeddedYieldKeyword.ts(1,15): error TS1005: '(' expected.
33

44

5-
!!! error TS2318: Cannot find global type 'Generator'.
5+
!!! error TS2318: Cannot find global type 'IterableIterator'.
66
==== tests/cases/conformance/es6/templates/templateStringWithEmbeddedYieldKeyword.ts (1 errors) ====
77
function* gen {
88
~

tests/baselines/reference/types.forAwait.es2018.3.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
error TS2318: Cannot find global type 'AsyncGenerator'.
1+
error TS2318: Cannot find global type 'AsyncIterableIterator'.
22
tests/cases/conformance/types/forAwait/types.forAwait.es2018.3.ts(3,27): error TS2504: Type '{}' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.
33
tests/cases/conformance/types/forAwait/types.forAwait.es2018.3.ts(5,21): error TS2504: Type '{}' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.
44
tests/cases/conformance/types/forAwait/types.forAwait.es2018.3.ts(10,27): error TS2504: Type '{}' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.
55
tests/cases/conformance/types/forAwait/types.forAwait.es2018.3.ts(12,21): error TS2504: Type '{}' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.
66

77

8-
!!! error TS2318: Cannot find global type 'AsyncGenerator'.
8+
!!! error TS2318: Cannot find global type 'AsyncIterableIterator'.
99
==== tests/cases/conformance/types/forAwait/types.forAwait.es2018.3.ts (4 errors) ====
1010
async function f1() {
1111
let y: number;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// @target: esnext
2+
// @lib: es5,es2015.iterable
3+
// @noemit: true
4+
// @strict: true
5+
6+
// Allow generators to fallback to IterableIterator if they do not need a type for the sent value while in strictNullChecks mode.
7+
function* f() {
8+
yield 1;
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @target: esnext
2+
// @lib: es5
3+
// @noemit: true
4+
// @strict: true
5+
6+
// Allow generators to fallback to IterableIterator if they do not need a type for the sent value while in strictNullChecks mode.
7+
// Report an error if IterableIterator cannot be found.
8+
function* f() {
9+
yield 1;
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @target: esnext
2+
// @lib: es5,es2015.iterable
3+
// @noemit: true
4+
// @strict: true
5+
6+
// Do not allow generators to fallback to IterableIterator while in strictNullChecks mode if they need a type for the sent value.
7+
// NOTE: In non-strictNullChecks mode, `undefined` (the default sent value) is assignable to everything.
8+
function* f() {
9+
const x: string = yield 1;
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @target: esnext
2+
// @lib: es5,es2015.iterable
3+
// @noemit: true
4+
// @strict: false
5+
6+
// Allow generators to fallback to IterableIterator if they are not in strictNullChecks mode
7+
// NOTE: In non-strictNullChecks mode, `undefined` (the default sent value) is assignable to everything.
8+
function* f() {
9+
const x: string = yield 1;
10+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// @target: esnext
2+
// @lib: es5,es2015.iterable
3+
// @noemit: true
4+
// @strict: true
5+
6+
// Allow generators to fallback to IterableIterator if they do not need a type for the sent value while in strictNullChecks mode.
7+
function* f(): IterableIterator<number> {
8+
yield 1;
9+
}

0 commit comments

Comments
 (0)