Skip to content

Commit 2fbcd36

Browse files
author
Andy Hanson
committed
Treat 'yield;' as 'yield undefined;'
1 parent dfa1a90 commit 2fbcd36

31 files changed

+166
-138
lines changed

src/compiler/checker.ts

Lines changed: 43 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -18380,27 +18380,23 @@ namespace ts {
1838018380

1838118381
function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, checkMode: CheckMode): Type[] {
1838218382
const aggregatedTypes: Type[] = [];
18383-
const functionFlags = getFunctionFlags(func);
18383+
const isAsync = (getFunctionFlags(func) & FunctionFlags.Async) !== 0;
1838418384
forEachYieldExpression(<Block>func.body, yieldExpression => {
18385-
const expr = yieldExpression.expression;
18386-
if (expr) {
18387-
let type = checkExpressionCached(expr, checkMode);
18388-
if (yieldExpression.asteriskToken) {
18389-
// A yield* expression effectively yields everything that its operand yields
18390-
type = checkIteratedTypeOrElementType(type, yieldExpression.expression, /*allowStringInput*/ false, (functionFlags & FunctionFlags.Async) !== 0);
18391-
}
18392-
if (functionFlags & FunctionFlags.Async) {
18393-
type = checkAwaitedType(type, expr, yieldExpression.asteriskToken
18394-
? Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member
18395-
: Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
18396-
}
18397-
pushIfUnique(aggregatedTypes, type);
18398-
}
18385+
pushIfUnique(aggregatedTypes, getYieldedTypeOfYieldExpression(yieldExpression, isAsync, checkMode));
1839918386
});
18400-
1840118387
return aggregatedTypes;
1840218388
}
1840318389

18390+
function getYieldedTypeOfYieldExpression(node: YieldExpression, isAsync: boolean, checkMode?: CheckMode): Type {
18391+
const errorNode = node.expression || node;
18392+
const expressionType = node.expression ? checkExpressionCached(node.expression, checkMode) : undefinedType;
18393+
// A yield* expression effectively yields everything that its operand yields
18394+
const yieldedType = node.asteriskToken ? checkIteratedTypeOrElementType(expressionType, errorNode, /*allowStringInput*/ false, isAsync) : expressionType;
18395+
return !isAsync ? yieldedType : getAwaitedType(yieldedType, errorNode, node.asteriskToken
18396+
? Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member
18397+
: Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
18398+
}
18399+
1840418400
function isExhaustiveSwitchStatement(node: SwitchStatement): boolean {
1840518401
if (!node.possiblyExhaustive) {
1840618402
return false;
@@ -19353,62 +19349,40 @@ namespace ts {
1935319349
}
1935419350
}
1935519351

19356-
if (node.expression) {
19357-
const func = getContainingFunction(node);
19358-
// If the user's code is syntactically correct, the func should always have a star. After all,
19359-
// we are in a yield context.
19360-
const functionFlags = func && getFunctionFlags(func);
19361-
if (node.asteriskToken) {
19362-
// Async generator functions prior to ESNext require the __await, __asyncDelegator,
19363-
// and __asyncValues helpers
19364-
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator &&
19365-
languageVersion < ScriptTarget.ESNext) {
19366-
checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncDelegatorIncludes);
19367-
}
19368-
19369-
// Generator functions prior to ES2015 require the __values helper
19370-
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Generator &&
19371-
languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
19372-
checkExternalEmitHelpers(node, ExternalEmitHelpers.Values);
19373-
}
19374-
}
19375-
19376-
if (functionFlags & FunctionFlags.Generator) {
19377-
const expressionType = checkExpressionCached(node.expression);
19378-
let expressionElementType: Type;
19379-
const nodeIsYieldStar = !!node.asteriskToken;
19380-
if (nodeIsYieldStar) {
19381-
expressionElementType = checkIteratedTypeOrElementType(expressionType, node.expression, /*allowStringInput*/ false, (functionFlags & FunctionFlags.Async) !== 0);
19382-
}
19383-
19384-
// There is no point in doing an assignability check if the function
19385-
// has no explicit return type because the return type is directly computed
19386-
// from the yield expressions.
19387-
const returnType = getEffectiveReturnTypeNode(func);
19388-
if (returnType) {
19389-
const signatureElementType = getIteratedTypeOfGenerator(getTypeFromTypeNode(returnType), (functionFlags & FunctionFlags.Async) !== 0) || anyType;
19390-
if (nodeIsYieldStar) {
19391-
checkTypeAssignableTo(
19392-
functionFlags & FunctionFlags.Async
19393-
? getAwaitedType(expressionElementType, node.expression, Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
19394-
: expressionElementType,
19395-
signatureElementType,
19396-
node.expression,
19397-
/*headMessage*/ undefined);
19398-
}
19399-
else {
19400-
checkTypeAssignableTo(
19401-
functionFlags & FunctionFlags.Async
19402-
? getAwaitedType(expressionType, node.expression, Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
19403-
: expressionType,
19404-
signatureElementType,
19405-
node.expression,
19406-
/*headMessage*/ undefined);
19407-
}
19408-
}
19352+
const func = getContainingFunction(node);
19353+
const functionFlags = func ? getFunctionFlags(func) : FunctionFlags.Normal;
19354+
19355+
if (!(functionFlags & FunctionFlags.Generator)) {
19356+
// If the user's code is syntactically correct, the func should always have a star. After all, we are in a yield context.
19357+
return anyType;
19358+
}
19359+
19360+
if (node.asteriskToken) {
19361+
// Async generator functions prior to ESNext require the __await, __asyncDelegator,
19362+
// and __asyncValues helpers
19363+
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator &&
19364+
languageVersion < ScriptTarget.ESNext) {
19365+
checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncDelegatorIncludes);
19366+
}
19367+
19368+
// Generator functions prior to ES2015 require the __values helper
19369+
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Generator &&
19370+
languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
19371+
checkExternalEmitHelpers(node, ExternalEmitHelpers.Values);
1940919372
}
1941019373
}
1941119374

19375+
const isAsync = (functionFlags & FunctionFlags.Async) !== 0;
19376+
const yieldedType = getYieldedTypeOfYieldExpression(node, isAsync);
19377+
// There is no point in doing an assignability check if the function
19378+
// has no explicit return type because the return type is directly computed
19379+
// from the yield expressions.
19380+
const returnType = getEffectiveReturnTypeNode(func);
19381+
if (returnType) {
19382+
const signatureElementType = getIteratedTypeOfGenerator(getTypeFromTypeNode(returnType), isAsync) || anyType;
19383+
checkTypeAssignableTo(yieldedType, signatureElementType, node.expression || node, /*headMessage*/ undefined);
19384+
}
19385+
1941219386
// Both yield and yield* expressions have type 'any'
1941319387
return anyType;
1941419388
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
=== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration9_es6.ts ===
22
function * foo() {
3-
>foo : () => IterableIterator<any>
3+
>foo : () => IterableIterator<undefined>
44

55
var v = { [yield]: foo }
6-
>v : { [x: number]: () => IterableIterator<any>; }
7-
>{ [yield]: foo } : { [x: number]: () => IterableIterator<any>; }
8-
>[yield] : () => IterableIterator<any>
6+
>v : { [x: number]: () => IterableIterator<undefined>; }
7+
>{ [yield]: foo } : { [x: number]: () => IterableIterator<undefined>; }
8+
>[yield] : () => IterableIterator<undefined>
99
>yield : any
10-
>foo : () => IterableIterator<any>
10+
>foo : () => IterableIterator<undefined>
1111
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
=== tests/cases/conformance/es6/yieldExpressions/YieldExpression13_es6.ts ===
22
function* foo() { yield }
3-
>foo : () => IterableIterator<any>
3+
>foo : () => IterableIterator<undefined>
44
>yield : any
55

tests/baselines/reference/YieldExpression3_es6.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== tests/cases/conformance/es6/yieldExpressions/YieldExpression3_es6.ts ===
22
function* foo() {
3-
>foo : () => IterableIterator<any>
3+
>foo : () => IterableIterator<undefined>
44

55
yield
66
>yield : any

tests/baselines/reference/YieldExpression4_es6.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== tests/cases/conformance/es6/yieldExpressions/YieldExpression4_es6.ts ===
22
function* foo() {
3-
>foo : () => IterableIterator<any>
3+
>foo : () => IterableIterator<undefined>
44

55
yield;
66
>yield : any

tests/baselines/reference/emitter.asyncGenerators.classMethods.es2015.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class C2 {
1111
>C2 : C2
1212

1313
async * f() {
14-
>f : () => AsyncIterableIterator<any>
14+
>f : () => AsyncIterableIterator<undefined>
1515

1616
const x = yield;
1717
>x : any

tests/baselines/reference/emitter.asyncGenerators.classMethods.es5.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class C2 {
1111
>C2 : C2
1212

1313
async * f() {
14-
>f : () => AsyncIterableIterator<any>
14+
>f : () => AsyncIterableIterator<undefined>
1515

1616
const x = yield;
1717
>x : any

tests/baselines/reference/emitter.asyncGenerators.classMethods.esnext.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class C2 {
1111
>C2 : C2
1212

1313
async * f() {
14-
>f : () => AsyncIterableIterator<any>
14+
>f : () => AsyncIterableIterator<undefined>
1515

1616
const x = yield;
1717
>x : any

tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es2015.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ async function * f1() {
44
}
55
=== tests/cases/conformance/emitter/es2015/asyncGenerators/F2.ts ===
66
async function * f2() {
7-
>f2 : () => AsyncIterableIterator<any>
7+
>f2 : () => AsyncIterableIterator<undefined>
88

99
const x = yield;
1010
>x : any

tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es5.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ async function * f1() {
44
}
55
=== tests/cases/conformance/emitter/es5/asyncGenerators/F2.ts ===
66
async function * f2() {
7-
>f2 : () => AsyncIterableIterator<any>
7+
>f2 : () => AsyncIterableIterator<undefined>
88

99
const x = yield;
1010
>x : any

tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.esnext.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ async function * f1() {
44
}
55
=== tests/cases/conformance/emitter/esnext/asyncGenerators/F2.ts ===
66
async function * f2() {
7-
>f2 : () => AsyncIterableIterator<any>
7+
>f2 : () => AsyncIterableIterator<undefined>
88

99
const x = yield;
1010
>x : any

tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es2015.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ const f1 = async function * () {
55
}
66
=== tests/cases/conformance/emitter/es2015/asyncGenerators/F2.ts ===
77
const f2 = async function * () {
8-
>f2 : () => AsyncIterableIterator<any>
9-
>async function * () { const x = yield;} : () => AsyncIterableIterator<any>
8+
>f2 : () => AsyncIterableIterator<undefined>
9+
>async function * () { const x = yield;} : () => AsyncIterableIterator<undefined>
1010

1111
const x = yield;
1212
>x : any

tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es5.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ const f1 = async function * () {
55
}
66
=== tests/cases/conformance/emitter/es5/asyncGenerators/F2.ts ===
77
const f2 = async function * () {
8-
>f2 : () => AsyncIterableIterator<any>
9-
>async function * () { const x = yield;} : () => AsyncIterableIterator<any>
8+
>f2 : () => AsyncIterableIterator<undefined>
9+
>async function * () { const x = yield;} : () => AsyncIterableIterator<undefined>
1010

1111
const x = yield;
1212
>x : any

tests/baselines/reference/emitter.asyncGenerators.functionExpressions.esnext.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ const f1 = async function * () {
55
}
66
=== tests/cases/conformance/emitter/esnext/asyncGenerators/F2.ts ===
77
const f2 = async function * () {
8-
>f2 : () => AsyncIterableIterator<any>
9-
>async function * () { const x = yield;} : () => AsyncIterableIterator<any>
8+
>f2 : () => AsyncIterableIterator<undefined>
9+
>async function * () { const x = yield;} : () => AsyncIterableIterator<undefined>
1010

1111
const x = yield;
1212
>x : any

tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es2015.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ const o1 = {
99
}
1010
=== tests/cases/conformance/emitter/es2015/asyncGenerators/O2.ts ===
1111
const o2 = {
12-
>o2 : { f(): AsyncIterableIterator<any>; }
13-
>{ async * f() { const x = yield; }} : { f(): AsyncIterableIterator<any>; }
12+
>o2 : { f(): AsyncIterableIterator<undefined>; }
13+
>{ async * f() { const x = yield; }} : { f(): AsyncIterableIterator<undefined>; }
1414

1515
async * f() {
16-
>f : () => AsyncIterableIterator<any>
16+
>f : () => AsyncIterableIterator<undefined>
1717

1818
const x = yield;
1919
>x : any

tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es5.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ const o1 = {
99
}
1010
=== tests/cases/conformance/emitter/es5/asyncGenerators/O2.ts ===
1111
const o2 = {
12-
>o2 : { f(): AsyncIterableIterator<any>; }
13-
>{ async * f() { const x = yield; }} : { f(): AsyncIterableIterator<any>; }
12+
>o2 : { f(): AsyncIterableIterator<undefined>; }
13+
>{ async * f() { const x = yield; }} : { f(): AsyncIterableIterator<undefined>; }
1414

1515
async * f() {
16-
>f : () => AsyncIterableIterator<any>
16+
>f : () => AsyncIterableIterator<undefined>
1717

1818
const x = yield;
1919
>x : any

tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.esnext.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ const o1 = {
99
}
1010
=== tests/cases/conformance/emitter/esnext/asyncGenerators/O2.ts ===
1111
const o2 = {
12-
>o2 : { f(): AsyncIterableIterator<any>; }
13-
>{ async * f() { const x = yield; }} : { f(): AsyncIterableIterator<any>; }
12+
>o2 : { f(): AsyncIterableIterator<undefined>; }
13+
>{ async * f() { const x = yield; }} : { f(): AsyncIterableIterator<undefined>; }
1414

1515
async * f() {
16-
>f : () => AsyncIterableIterator<any>
16+
>f : () => AsyncIterableIterator<undefined>
1717

1818
const x = yield;
1919
>x : any

tests/baselines/reference/generatorES6InAMDModule.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== tests/cases/compiler/generatorES6InAMDModule.ts ===
22
export function* foo() {
3-
>foo : () => IterableIterator<any>
3+
>foo : () => IterableIterator<undefined>
44

55
yield
66
>yield : any

tests/baselines/reference/generatorES6_1.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== tests/cases/compiler/generatorES6_1.ts ===
22
function* foo() {
3-
>foo : () => IterableIterator<any>
3+
>foo : () => IterableIterator<undefined>
44

55
yield
66
>yield : any

tests/baselines/reference/generatorTypeCheck48.errors.txt

Lines changed: 0 additions & 9 deletions
This file was deleted.

tests/baselines/reference/generatorTypeCheck48.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck48.ts ===
22
function* g() {
3-
>g : () => IterableIterator<any>
3+
>g : () => IterableIterator<undefined>
44

55
yield;
66
>yield : any

tests/baselines/reference/parser.asyncGenerators.classMethods.esnext.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class C13 {
140140
>C13 : C13
141141

142142
async * f() {
143-
>f : () => AsyncIterableIterator<any>
143+
>f : () => AsyncIterableIterator<undefined>
144144

145145
yield;
146146
>yield : any
@@ -250,7 +250,7 @@ class C22 {
250250
>C22 : C22
251251

252252
async * f() {
253-
>f : () => AsyncIterableIterator<any>
253+
>f : () => AsyncIterableIterator<undefined>
254254

255255
const x = { [yield]: 1 };
256256
>x : { [x: number]: number; }

tests/baselines/reference/parser.asyncGenerators.functionDeclarations.esnext.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ async function * f12() {
8888
}
8989
=== tests/cases/conformance/parser/ecmascriptnext/asyncGenerators/yieldIsOk.ts ===
9090
async function * f13() {
91-
>f13 : () => AsyncIterableIterator<any>
91+
>f13 : () => AsyncIterableIterator<undefined>
9292

9393
yield;
9494
>yield : any
@@ -157,7 +157,7 @@ async function * f20() {
157157
}
158158
=== tests/cases/conformance/parser/ecmascriptnext/asyncGenerators/yieldInNestedComputedPropertyIsOk.ts ===
159159
async function * f21() {
160-
>f21 : () => AsyncIterableIterator<any>
160+
>f21 : () => AsyncIterableIterator<undefined>
161161

162162
const x = { [yield]: 1 };
163163
>x : { [x: number]: number; }

tests/baselines/reference/parser.asyncGenerators.functionExpressions.esnext.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ const f12 = async function * () {
113113
};
114114
=== tests/cases/conformance/parser/ecmascriptnext/asyncGenerators/yieldIsOk.ts ===
115115
const f13 = async function * () {
116-
>f13 : () => AsyncIterableIterator<any>
117-
>async function * () { yield;} : () => AsyncIterableIterator<any>
116+
>f13 : () => AsyncIterableIterator<undefined>
117+
>async function * () { yield;} : () => AsyncIterableIterator<undefined>
118118

119119
yield;
120120
>yield : any
@@ -198,8 +198,8 @@ const f20 = async function * () {
198198
};
199199
=== tests/cases/conformance/parser/ecmascriptnext/asyncGenerators/yieldInNestedComputedPropertyIsOk.ts ===
200200
const f21 = async function *() {
201-
>f21 : () => AsyncIterableIterator<any>
202-
>async function *() { const x = { [yield]: 1 };} : () => AsyncIterableIterator<any>
201+
>f21 : () => AsyncIterableIterator<undefined>
202+
>async function *() { const x = { [yield]: 1 };} : () => AsyncIterableIterator<undefined>
203203

204204
const x = { [yield]: 1 };
205205
>x : { [x: number]: number; }

0 commit comments

Comments
 (0)