From 66ad2e832e4abd9afc679a06b18754d94b65193a Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 26 Jan 2016 12:47:53 -0800 Subject: [PATCH] Added error when return type is not the global Promise --- src/compiler/checker.ts | 57 ++++++++++++++++++---------- src/compiler/diagnosticMessages.json | 4 ++ 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3c9e60cd191..b4f375d338c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2629,7 +2629,7 @@ namespace ts { } } else if (declaration.kind === SyntaxKind.Parameter) { - // If it's a parameter, see if the parent has a jsdoc comment with an @param + // If it's a parameter, see if the parent has a jsdoc comment with an @param // annotation. const paramTag = getCorrespondingJSDocParameterTag(declaration); if (paramTag && paramTag.typeExpression) { @@ -2644,7 +2644,7 @@ namespace ts { function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type { if (declaration.parserContextFlags & ParserContextFlags.JavaScriptFile) { // If this is a variable in a JavaScript file, then use the JSDoc type (if it has - // one as its type), otherwise fallback to the below standard TS codepaths to + // one as its type), otherwise fallback to the below standard TS codepaths to // try to figure it out. const type = getTypeForVariableLikeDeclarationFromJSDocComment(declaration); if (type && type !== unknownType) { @@ -4069,7 +4069,7 @@ namespace ts { const isJSConstructSignature = isJSDocConstructSignature(declaration); let returnType: Type = undefined; - // If this is a JSDoc construct signature, then skip the first parameter in the + // If this is a JSDoc construct signature, then skip the first parameter in the // parameter list. The first parameter represents the return type of the construct // signature. for (let i = isJSConstructSignature ? 1 : 0, n = declaration.parameters.length; i < n; i++) { @@ -4472,7 +4472,7 @@ namespace ts { } if (symbol.flags & SymbolFlags.Value && node.kind === SyntaxKind.JSDocTypeReference) { - // A JSDocTypeReference may have resolved to a value (as opposed to a type). In + // A JSDocTypeReference may have resolved to a value (as opposed to a type). In // that case, the type of this reference is just the type of the value we resolved // to. return getTypeOfSymbol(symbol); @@ -10479,7 +10479,7 @@ namespace ts { /* *TypeScript Specification 1.0 (6.3) - July 2014 - * An explicitly typed function whose return type isn't the Void type, + * An explicitly typed function whose return type isn't the Void type, * the Any type, or a union type containing the Void or Any type as a constituent * must have at least one return statement somewhere in its body. * An exception to this rule is if the function implementation consists of a single 'throw' statement. @@ -12508,6 +12508,33 @@ namespace ts { } } + /** + * Checks that the return type provided is an instantiation of the global Promise type + * and returns the awaited type of the return type. + */ + function checkCorrectPromiseType(returnType: Type, location: Node) { + if (returnType === unknownType) { + // The return type already had some other error, so we ignore and return + // the unknown type. + return unknownType; + } + + const globalPromiseType = getGlobalPromiseType(); + if (globalPromiseType === emptyGenericType + || globalPromiseType === getTargetType(returnType)) { + // Either we couldn't resolve the global promise type, which would have already + // reported an error, or we could resolve it and the return type is a valid type + // reference to the global type. In either case, we return the awaited type for + // the return type. + return checkAwaitedType(returnType, location, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); + } + + // The promise type was not a valid type reference to the global promise type, so we + // report an error and return the unknown type. + error(location, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T); + return unknownType; + } + /** * Checks the return type of an async function to ensure it is a compatible * Promise implementation. @@ -12522,6 +12549,11 @@ namespace ts { * callable `then` signature. */ function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type { + if (languageVersion >= ScriptTarget.ES6) { + const returnType = getTypeFromTypeNode(node.type); + return checkCorrectPromiseType(returnType, node.type); + } + const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(); if (globalPromiseConstructorLikeType === emptyObjectType) { // If we couldn't resolve the global PromiseConstructorLike type we cannot verify @@ -12563,21 +12595,6 @@ namespace ts { return unknownType; } - if (languageVersion >= ScriptTarget.ES6) { - const promisedType = getPromisedType(promiseType); - if (!promisedType) { - error(node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type, typeToString(promiseType)); - return unknownType; - } - - const promiseInstantiation = createPromiseType(promisedType); - if (!checkTypeAssignableTo(promiseInstantiation, promiseType, node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type)) { - return unknownType; - } - - return promisedType; - } - const promiseConstructor = getNodeLinks(node.type).resolvedSymbol; if (!promiseConstructor || !symbolIsValue(promiseConstructor)) { const typeName = promiseConstructor diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index b8defa6325b..f5e6254a040 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -195,6 +195,10 @@ "category": "Error", "code": 1063 }, + "The return type of an async function or method must be the global Promise.": { + "category": "Error", + "code": 1064 + }, "In ambient enum declarations member initializer must be constant expression.": { "category": "Error", "code": 1066