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

0 commit comments

Comments
 (0)