Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 35 additions & 17 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15889,7 +15889,7 @@ namespace ts {
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
// return type of the body should be unwrapped to its awaited type, which we will wrap in
// the native Promise<T> type later in this function.
type = checkAwaitedType(type, /*errorNode*/ func);
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);
}
}
else {
Expand Down Expand Up @@ -15963,6 +15963,11 @@ namespace ts {
// A yield* expression effectively yields everything that its operand yields
type = checkIteratedTypeOrElementType(type, yieldExpression.expression, /*allowStringInput*/ false, (functionFlags & FunctionFlags.Async) !== 0);
}
if (functionFlags & FunctionFlags.Async) {
type = checkAwaitedType(type, expr, yieldExpression.asteriskToken
? Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member
: Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
}
if (!contains(aggregatedTypes, type)) {
aggregatedTypes.push(type);
}
Expand Down Expand Up @@ -16012,7 +16017,7 @@ namespace ts {
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
// return type of the body should be unwrapped to its awaited type, which should be wrapped in
// the native Promise<T> type by the caller.
type = checkAwaitedType(type, func);
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);
}
if (type.flags & TypeFlags.Never) {
hasReturnOfTypeNever = true;
Expand Down Expand Up @@ -16189,7 +16194,7 @@ namespace ts {
const exprType = checkExpression(<Expression>node.body);
if (returnOrPromisedType) {
if ((functionFlags & FunctionFlags.AsyncOrAsyncGenerator) === FunctionFlags.Async) { // Async function
const awaitedType = checkAwaitedType(exprType, node.body);
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);
checkTypeAssignableTo(awaitedType, returnOrPromisedType, node.body);
}
else { // Normal function
Expand Down Expand Up @@ -16305,7 +16310,7 @@ namespace ts {
}

const operandType = checkExpression(node.expression);
return checkAwaitedType(operandType, node);
return checkAwaitedType(operandType, node, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
}

function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type {
Expand Down Expand Up @@ -16952,10 +16957,22 @@ namespace ts {
if (func.type) {
const signatureElementType = getIteratedTypeOfGenerator(getTypeFromTypeNode(func.type), (functionFlags & FunctionFlags.Async) !== 0) || anyType;
if (nodeIsYieldStar) {
checkTypeAssignableTo(expressionElementType, signatureElementType, node.expression, /*headMessage*/ undefined);
checkTypeAssignableTo(
functionFlags & FunctionFlags.Async
? 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)
: expressionElementType,
signatureElementType,
node.expression,
/*headMessage*/ undefined);
}
else {
checkTypeAssignableTo(expressionType, signatureElementType, node.expression, /*headMessage*/ undefined);
checkTypeAssignableTo(
functionFlags & FunctionFlags.Async
? 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)
: expressionType,
signatureElementType,
node.expression,
/*headMessage*/ undefined);
}
}
}
Expand Down Expand Up @@ -18290,9 +18307,9 @@ namespace ts {
}
}

function getAwaitedTypeOfPromise(type: Type, errorNode?: Node): Type | undefined {
function getAwaitedTypeOfPromise(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage): Type | undefined {
const promisedType = getPromisedTypeOfPromise(type, errorNode);
return promisedType && getAwaitedType(promisedType, errorNode);
return promisedType && getAwaitedType(promisedType, errorNode, diagnosticMessage);
}

/**
Expand Down Expand Up @@ -18360,11 +18377,11 @@ namespace ts {
* Promise-like type; otherwise, it is the type of the expression. This is used to reflect
* The runtime behavior of the `await` keyword.
*/
function checkAwaitedType(type: Type, errorNode: Node): Type {
return getAwaitedType(type, errorNode) || unknownType;
function checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage): Type {
return getAwaitedType(type, errorNode, diagnosticMessage) || unknownType;
}

function getAwaitedType(type: Type, errorNode?: Node): Type | undefined {
function getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage): Type | undefined {
const typeAsAwaitable = <PromiseOrAwaitableType>type;
if (typeAsAwaitable.awaitedTypeOfType) {
return typeAsAwaitable.awaitedTypeOfType;
Expand All @@ -18377,7 +18394,7 @@ namespace ts {
if (type.flags & TypeFlags.Union) {
let types: Type[];
for (const constituentType of (<UnionType>type).types) {
types = append(types, getAwaitedType(constituentType, errorNode));
types = append(types, getAwaitedType(constituentType, errorNode, diagnosticMessage));
}

if (!types) {
Expand Down Expand Up @@ -18431,7 +18448,7 @@ namespace ts {
// Keep track of the type we're about to unwrap to avoid bad recursive promise types.
// See the comments above for more information.
awaitedTypeStack.push(type.id);
const awaitedType = getAwaitedType(promisedType, errorNode);
const awaitedType = getAwaitedType(promisedType, errorNode, diagnosticMessage);
awaitedTypeStack.pop();

if (!awaitedType) {
Expand Down Expand Up @@ -18459,7 +18476,8 @@ namespace ts {
const thenFunction = getTypeOfPropertyOfType(type, "then");
if (thenFunction && getSignaturesOfType(thenFunction, SignatureKind.Call).length > 0) {
if (errorNode) {
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);
Debug.assert(!!diagnosticMessage);
error(errorNode, diagnosticMessage);
}
return undefined;
}
Expand Down Expand Up @@ -18570,7 +18588,7 @@ namespace ts {
}

// Get and return the awaited type of the return type.
return checkAwaitedType(returnType, node);
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);
}

/** Check a decorator */
Expand Down Expand Up @@ -19854,7 +19872,7 @@ namespace ts {

// For an async iterator, we must get the awaited type of the return type.
if (isAsyncIterator) {
nextResult = getAwaitedTypeOfPromise(nextResult, errorNode);
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);
if (isTypeAny(nextResult)) {
return undefined;
}
Expand Down Expand Up @@ -19945,7 +19963,7 @@ namespace ts {
else if (func.type || isGetAccessorWithAnnotatedSetAccessor(func)) {
if (functionFlags & FunctionFlags.Async) { // Async function
const promisedType = getPromisedTypeOfPromise(returnType);
const awaitedType = checkAwaitedType(exprType, node);
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);
if (promisedType) {
// If the function has a return type, but promisedType is
// undefined, an error will be reported in checkAsyncFunctionReturnType
Expand Down
14 changes: 13 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
"category": "Error",
"code": 1057
},
"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.": {
"The return type of an async function must either be a valid promise or must not contain a callable 'then' member.": {
"category": "Error",
"code": 1058
},
Expand Down Expand Up @@ -867,6 +867,18 @@
"category": "Error",
"code": 1319
},
"Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.": {
"category": "Error",
"code": 1320
},
"Type of 'yield' operand in an async generator must either be a valid promise or must not contain a callable 'then' member.": {
"category": "Error",
"code": 1321
},
"Type of iterated elements of a 'yield*' operand must either be a valid promise or must not contain a callable 'then' member.": {
"category": "Error",
"code": 1322
},
"Duplicate identifier '{0}'.": {
"category": "Error",
"code": 2300
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/transformers/esnext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -891,7 +891,8 @@ namespace ts {
function verb(n) { return function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]), next(); }); }; }
function next() { if (!c && q.length) resume((c = q.shift())[0], c[1]); }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(c[3], e); } }
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); }
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); }
function _yield(value) { settle(c[2], { value: value, done: false }); }
function delegate(r) { step(r.done ? r : { value: ["yield", r.value], done: false }); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1
Types of property 'then' are incompatible.
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>'.
Type 'void' is not assignable to type 'PromiseLike<any>'.
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.
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.
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.
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.


==== tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration15_es5.ts (8 errors) ====
Expand Down Expand Up @@ -47,13 +47,13 @@ tests/cases/conformance/async/es5/functionDeclarations/asyncFunctionDeclaration1
async function fn12() { return obj; } // valid: Promise<{ then: string; }>
async function fn13() { return thenable; } // error
~~~~
!!! 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.
!!! error TS1058: The return type of an async function must either be a valid promise or must not contain a callable 'then' member.
async function fn14() { await 1; } // valid: Promise<void>
async function fn15() { await null; } // valid: Promise<void>
async function fn16() { await undefined; } // valid: Promise<void>
async function fn17() { await a; } // valid: Promise<void>
async function fn18() { await obj; } // valid: Promise<void>
async function fn19() { await thenable; } // error
~~~~~~~~~~~~~~
!!! 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.
!!! error TS1320: Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.

Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1
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.
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.
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.
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.
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.
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.
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.


==== tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts (7 errors) ====
Expand Down Expand Up @@ -36,13 +36,13 @@ tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration1
async function fn12() { return obj; } // valid: Promise<{ then: string; }>
async function fn13() { return thenable; } // error
~~~~
!!! 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.
!!! error TS1058: The return type of an async function must either be a valid promise or must not contain a callable 'then' member.
async function fn14() { await 1; } // valid: Promise<void>
async function fn15() { await null; } // valid: Promise<void>
async function fn16() { await undefined; } // valid: Promise<void>
async function fn17() { await a; } // valid: Promise<void>
async function fn18() { await obj; } // valid: Promise<void>
async function fn19() { await thenable; } // error
~~~~~~~~~~~~~~
!!! 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.
!!! error TS1320: Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.

Loading