Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2ca1de5

Browse filesBrowse files
authored
Merge pull request microsoft#15241 from Microsoft/fix15205
Yield in async generator should implicitly unwrap operand
2 parents e36d8d5 + 64e2c29 commit 2ca1de5
Copy full SHA for 2ca1de5

20 files changed

+690-201Lines changed: 690 additions & 201 deletions
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/checker.ts‎

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+35-17Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15832,7 +15832,7 @@ namespace ts {
1583215832
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
1583315833
// return type of the body should be unwrapped to its awaited type, which we will wrap in
1583415834
// the native Promise<T> type later in this function.
15835-
type = checkAwaitedType(type, /*errorNode*/ func);
15835+
type = checkAwaitedType(type, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
1583615836
}
1583715837
}
1583815838
else {
@@ -15906,6 +15906,11 @@ namespace ts {
1590615906
// A yield* expression effectively yields everything that its operand yields
1590715907
type = checkIteratedTypeOrElementType(type, yieldExpression.expression, /*allowStringInput*/ false, (functionFlags & FunctionFlags.Async) !== 0);
1590815908
}
15909+
if (functionFlags & FunctionFlags.Async) {
15910+
type = checkAwaitedType(type, expr, yieldExpression.asteriskToken
15911+
? Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member
15912+
: Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
15913+
}
1590915914
if (!contains(aggregatedTypes, type)) {
1591015915
aggregatedTypes.push(type);
1591115916
}
@@ -15955,7 +15960,7 @@ namespace ts {
1595515960
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
1595615961
// return type of the body should be unwrapped to its awaited type, which should be wrapped in
1595715962
// the native Promise<T> type by the caller.
15958-
type = checkAwaitedType(type, func);
15963+
type = checkAwaitedType(type, func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
1595915964
}
1596015965
if (type.flags & TypeFlags.Never) {
1596115966
hasReturnOfTypeNever = true;
@@ -16132,7 +16137,7 @@ namespace ts {
1613216137
const exprType = checkExpression(<Expression>node.body);
1613316138
if (returnOrPromisedType) {
1613416139
if ((functionFlags & FunctionFlags.AsyncOrAsyncGenerator) === FunctionFlags.Async) { // Async function
16135-
const awaitedType = checkAwaitedType(exprType, node.body);
16140+
const awaitedType = checkAwaitedType(exprType, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
1613616141
checkTypeAssignableTo(awaitedType, returnOrPromisedType, node.body);
1613716142
}
1613816143
else { // Normal function
@@ -16248,7 +16253,7 @@ namespace ts {
1624816253
}
1624916254

1625016255
const operandType = checkExpression(node.expression);
16251-
return checkAwaitedType(operandType, node);
16256+
return checkAwaitedType(operandType, node, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
1625216257
}
1625316258

1625416259
function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type {
@@ -16895,10 +16900,22 @@ namespace ts {
1689516900
if (func.type) {
1689616901
const signatureElementType = getIteratedTypeOfGenerator(getTypeFromTypeNode(func.type), (functionFlags & FunctionFlags.Async) !== 0) || anyType;
1689716902
if (nodeIsYieldStar) {
16898-
checkTypeAssignableTo(expressionElementType, signatureElementType, node.expression, /*headMessage*/ undefined);
16903+
checkTypeAssignableTo(
16904+
functionFlags & FunctionFlags.Async
16905+
? 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)
16906+
: expressionElementType,
16907+
signatureElementType,
16908+
node.expression,
16909+
/*headMessage*/ undefined);
1689916910
}
1690016911
else {
16901-
checkTypeAssignableTo(expressionType, signatureElementType, node.expression, /*headMessage*/ undefined);
16912+
checkTypeAssignableTo(
16913+
functionFlags & FunctionFlags.Async
16914+
? 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)
16915+
: expressionType,
16916+
signatureElementType,
16917+
node.expression,
16918+
/*headMessage*/ undefined);
1690216919
}
1690316920
}
1690416921
}
@@ -18233,9 +18250,9 @@ namespace ts {
1823318250
}
1823418251
}
1823518252

18236-
function getAwaitedTypeOfPromise(type: Type, errorNode?: Node): Type | undefined {
18253+
function getAwaitedTypeOfPromise(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage): Type | undefined {
1823718254
const promisedType = getPromisedTypeOfPromise(type, errorNode);
18238-
return promisedType && getAwaitedType(promisedType, errorNode);
18255+
return promisedType && getAwaitedType(promisedType, errorNode, diagnosticMessage);
1823918256
}
1824018257

1824118258
/**
@@ -18303,11 +18320,11 @@ namespace ts {
1830318320
* Promise-like type; otherwise, it is the type of the expression. This is used to reflect
1830418321
* The runtime behavior of the `await` keyword.
1830518322
*/
18306-
function checkAwaitedType(type: Type, errorNode: Node): Type {
18307-
return getAwaitedType(type, errorNode) || unknownType;
18323+
function checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage): Type {
18324+
return getAwaitedType(type, errorNode, diagnosticMessage) || unknownType;
1830818325
}
1830918326

18310-
function getAwaitedType(type: Type, errorNode?: Node): Type | undefined {
18327+
function getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage): Type | undefined {
1831118328
const typeAsAwaitable = <PromiseOrAwaitableType>type;
1831218329
if (typeAsAwaitable.awaitedTypeOfType) {
1831318330
return typeAsAwaitable.awaitedTypeOfType;
@@ -18320,7 +18337,7 @@ namespace ts {
1832018337
if (type.flags & TypeFlags.Union) {
1832118338
let types: Type[];
1832218339
for (const constituentType of (<UnionType>type).types) {
18323-
types = append(types, getAwaitedType(constituentType, errorNode));
18340+
types = append(types, getAwaitedType(constituentType, errorNode, diagnosticMessage));
1832418341
}
1832518342

1832618343
if (!types) {
@@ -18374,7 +18391,7 @@ namespace ts {
1837418391
// Keep track of the type we're about to unwrap to avoid bad recursive promise types.
1837518392
// See the comments above for more information.
1837618393
awaitedTypeStack.push(type.id);
18377-
const awaitedType = getAwaitedType(promisedType, errorNode);
18394+
const awaitedType = getAwaitedType(promisedType, errorNode, diagnosticMessage);
1837818395
awaitedTypeStack.pop();
1837918396

1838018397
if (!awaitedType) {
@@ -18402,7 +18419,8 @@ namespace ts {
1840218419
const thenFunction = getTypeOfPropertyOfType(type, "then");
1840318420
if (thenFunction && getSignaturesOfType(thenFunction, SignatureKind.Call).length > 0) {
1840418421
if (errorNode) {
18405-
error(errorNode, Diagnostics.Type_used_as_operand_to_await_or_the_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
18422+
Debug.assert(!!diagnosticMessage);
18423+
error(errorNode, diagnosticMessage);
1840618424
}
1840718425
return undefined;
1840818426
}
@@ -18513,7 +18531,7 @@ namespace ts {
1851318531
}
1851418532

1851518533
// Get and return the awaited type of the return type.
18516-
return checkAwaitedType(returnType, node);
18534+
return checkAwaitedType(returnType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
1851718535
}
1851818536

1851918537
/** Check a decorator */
@@ -19794,7 +19812,7 @@ namespace ts {
1979419812

1979519813
// For an async iterator, we must get the awaited type of the return type.
1979619814
if (isAsyncIterator) {
19797-
nextResult = getAwaitedTypeOfPromise(nextResult, errorNode);
19815+
nextResult = getAwaitedTypeOfPromise(nextResult, errorNode, Diagnostics.The_type_returned_by_the_next_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property);
1979819816
if (isTypeAny(nextResult)) {
1979919817
return undefined;
1980019818
}
@@ -19885,7 +19903,7 @@ namespace ts {
1988519903
else if (func.type || isGetAccessorWithAnnotatedSetAccessor(func)) {
1988619904
if (functionFlags & FunctionFlags.Async) { // Async function
1988719905
const promisedType = getPromisedTypeOfPromise(returnType);
19888-
const awaitedType = checkAwaitedType(exprType, node);
19906+
const awaitedType = checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
1988919907
if (promisedType) {
1989019908
// If the function has a return type, but promisedType is
1989119909
// undefined, an error will be reported in checkAsyncFunctionReturnType
Collapse file

‎src/compiler/diagnosticMessages.json‎

Copy file name to clipboardExpand all lines: src/compiler/diagnosticMessages.json
+13-1Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@
175175
"category": "Error",
176176
"code": 1057
177177
},
178-
"Type used as operand to 'await' or the return type of an async function must either be a valid promise or must not contain a callable 'then' member.": {
178+
"The return type of an async function must either be a valid promise or must not contain a callable 'then' member.": {
179179
"category": "Error",
180180
"code": 1058
181181
},
@@ -867,6 +867,18 @@
867867
"category": "Error",
868868
"code": 1319
869869
},
870+
"Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.": {
871+
"category": "Error",
872+
"code": 1320
873+
},
874+
"Type of 'yield' operand in an async generator must either be a valid promise or must not contain a callable 'then' member.": {
875+
"category": "Error",
876+
"code": 1321
877+
},
878+
"Type of iterated elements of a 'yield*' operand must either be a valid promise or must not contain a callable 'then' member.": {
879+
"category": "Error",
880+
"code": 1322
881+
},
870882
"Duplicate identifier '{0}'.": {
871883
"category": "Error",
872884
"code": 2300
Collapse file

‎src/compiler/transformers/esnext.ts‎

Copy file name to clipboardExpand all lines: src/compiler/transformers/esnext.ts
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,8 @@ namespace ts {
891891
function verb(n) { return function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]), next(); }); }; }
892892
function next() { if (!c && q.length) resume((c = q.shift())[0], c[1]); }
893893
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(c[3], e); } }
894-
function step(r) { r.done ? settle(c[2], r) : r.value[0] === "yield" ? settle(c[2], { value: r.value[1], done: false }) : Promise.resolve(r.value[1]).then(r.value[0] === "delegate" ? delegate : fulfill, reject); }
894+
function step(r) { r.done ? settle(c[2], r) : Promise.resolve(r.value[1]).then(r.value[0] === "yield" ? _yield : r.value[0] === "delegate" ? delegate : fulfill, reject); }
895+
function _yield(value) { settle(c[2], { value: value, done: false }); }
895896
function delegate(r) { step(r.done ? r : { value: ["yield", r.value], done: false }); }
896897
function fulfill(value) { resume("next", value); }
897898
function reject(value) { resume("throw", value); }
Collapse file

‎tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt‎

Copy file name to clipboardExpand all lines: tests/baselines/reference/asyncFunctionDeclaration15_es5.errors.txt
+4-4Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1
88
Types of property 'then' are incompatible.
99
Type '() => void' is not assignable to type '<TResult1 = any, TResult2 = never>(onfulfilled?: (value: any) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => PromiseLike<TResult1 | TResult2>'.
1010
Type 'void' is not assignable to type 'PromiseLike<any>'.
11-
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(17,16): error TS1058: Type used as operand to 'await' or the return type of an async function must either be a valid promise or must not contain a callable 'then' member.
12-
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(23,25): error TS1058: Type used as operand to 'await' or the return type of an async function must either be a valid promise or must not contain a callable 'then' member.
11+
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(17,16): error TS1058: The return type of an async function must either be a valid promise or must not contain a callable 'then' member.
12+
tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts(23,25): error TS1320: Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.
1313

1414

1515
==== tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts (8 errors) ====
@@ -47,13 +47,13 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1
4747
async function fn12() { return obj; } // valid: Promise<{ then: string; }>
4848
async function fn13() { return thenable; } // error
4949
~~~~
50-
!!! error TS1058: Type used as operand to 'await' or the return type of an async function must either be a valid promise or must not contain a callable 'then' member.
50+
!!! error TS1058: The return type of an async function must either be a valid promise or must not contain a callable 'then' member.
5151
async function fn14() { await 1; } // valid: Promise<void>
5252
async function fn15() { await null; } // valid: Promise<void>
5353
async function fn16() { await undefined; } // valid: Promise<void>
5454
async function fn17() { await a; } // valid: Promise<void>
5555
async function fn18() { await obj; } // valid: Promise<void>
5656
async function fn19() { await thenable; } // error
5757
~~~~~~~~~~~~~~
58-
!!! error TS1058: Type used as operand to 'await' or the return type of an async function must either be a valid promise or must not contain a callable 'then' member.
58+
!!! error TS1320: Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.
5959

Collapse file

‎tests/baselines/reference/asyncFunctionDeclaration15_es6.errors.txt‎

Copy file name to clipboardExpand all lines: tests/baselines/reference/asyncFunctionDeclaration15_es6.errors.txt
+4-4Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1
33
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(8,23): error TS1064: The return type of an async function or method must be the global Promise<T> type.
44
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(9,23): error TS1064: The return type of an async function or method must be the global Promise<T> type.
55
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(10,23): error TS1064: The return type of an async function or method must be the global Promise<T> type.
6-
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(17,16): error TS1058: Type used as operand to 'await' or the return type of an async function must either be a valid promise or must not contain a callable 'then' member.
7-
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(23,25): error TS1058: Type used as operand to 'await' or the return type of an async function must either be a valid promise or must not contain a callable 'then' member.
6+
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(17,16): error TS1058: The return type of an async function must either be a valid promise or must not contain a callable 'then' member.
7+
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(23,25): error TS1320: Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.
88

99

1010
==== tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts (7 errors) ====
@@ -36,13 +36,13 @@ tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1
3636
async function fn12() { return obj; } // valid: Promise<{ then: string; }>
3737
async function fn13() { return thenable; } // error
3838
~~~~
39-
!!! error TS1058: Type used as operand to 'await' or the return type of an async function must either be a valid promise or must not contain a callable 'then' member.
39+
!!! error TS1058: The return type of an async function must either be a valid promise or must not contain a callable 'then' member.
4040
async function fn14() { await 1; } // valid: Promise<void>
4141
async function fn15() { await null; } // valid: Promise<void>
4242
async function fn16() { await undefined; } // valid: Promise<void>
4343
async function fn17() { await a; } // valid: Promise<void>
4444
async function fn18() { await obj; } // valid: Promise<void>
4545
async function fn19() { await thenable; } // error
4646
~~~~~~~~~~~~~~
47-
!!! error TS1058: Type used as operand to 'await' or the return type of an async function must either be a valid promise or must not contain a callable 'then' member.
47+
!!! error TS1320: Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.
4848

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.