Get return type from @type tag (#25580)

* Get return type from `@type` tag

This only happens in the checker, where the type is easily accessible.
The syntax-based check in getEffectiveReturnTypeNode as a fast path, and
for other uses that don't want to make a call to getTypeFromTypeNode.

Fixes #25525

* Implement PR suggestions

* Error when type tag isn't callable

* Fix lint
This commit is contained in:
Nathan Shively-Sanders
2018-07-12 10:49:41 -07:00
committed by GitHub
parent 66e9aaac18
commit bd7b97ce61
18 changed files with 237 additions and 41 deletions

View File

@@ -7566,12 +7566,21 @@ namespace ts {
const setter = getDeclarationOfKind<AccessorDeclaration>(getSymbolOfNode(declaration), SyntaxKind.SetAccessor);
return getAnnotatedAccessorType(setter);
}
const typeFromTag = getReturnTypeOfTypeTag(declaration);
if (typeFromTag) {
return typeFromTag;
}
if (nodeIsMissing((<FunctionLikeDeclaration>declaration).body)) {
return anyType;
}
}
function getReturnTypeOfTypeTag(node: SignatureDeclaration | JSDocSignature) {
const typeTag = isInJavaScriptFile(node) ? getJSDocTypeTag(node) : undefined;
const signatures = typeTag && typeTag.typeExpression && getSignaturesOfType(getTypeFromTypeNode(typeTag.typeExpression), SignatureKind.Call);
return signatures && signatures.length === 1 ? getReturnTypeOfSignature(signatures[0]) : undefined;
}
function containsArgumentsReference(declaration: SignatureDeclaration): boolean {
const links = getNodeLinks(declaration);
if (links.containsArgumentsReference === undefined) {
@@ -20539,15 +20548,20 @@ namespace ts {
return type;
}
function getReturnOrPromisedType(node: FunctionLikeDeclaration | MethodSignature, functionFlags: FunctionFlags) {
const returnTypeNode = getEffectiveReturnTypeNode(node);
return returnTypeNode &&
((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async ?
checkAsyncFunctionReturnType(node, returnTypeNode) : // Async function
getTypeFromTypeNode(returnTypeNode)) || // AsyncGenerator function, Generator function, or normal function
getReturnTypeOfTypeTag(node); // type from JSDoc @type tag
}
function checkFunctionExpressionOrObjectLiteralMethodDeferred(node: ArrowFunction | FunctionExpression | MethodDeclaration) {
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
const functionFlags = getFunctionFlags(node);
const returnTypeNode = getEffectiveReturnTypeNode(node);
const returnOrPromisedType = returnTypeNode &&
((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async ?
checkAsyncFunctionReturnType(node) : // Async function
getTypeFromTypeNode(returnTypeNode)); // AsyncGenerator function, Generator function, or normal function
const returnOrPromisedType = getReturnOrPromisedType(node, functionFlags);
if ((functionFlags & FunctionFlags.Generator) === 0) { // Async function or normal function
// return is not necessary in the body of generators
@@ -20555,7 +20569,7 @@ namespace ts {
}
if (node.body) {
if (!returnTypeNode) {
if (!getEffectiveReturnTypeNode(node)) {
// There are some checks that are only performed in getReturnTypeFromBody, that may produce errors
// we need. An example is the noImplicitAny errors resulting from widening the return expression
// of a function. Because checking of function expression bodies is deferred, there was never an
@@ -22007,7 +22021,7 @@ namespace ts {
}
}
else if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) {
checkAsyncFunctionReturnType(<FunctionLikeDeclaration>node);
checkAsyncFunctionReturnType(<FunctionLikeDeclaration>node, returnTypeNode);
}
}
if (node.kind !== SyntaxKind.IndexSignature && node.kind !== SyntaxKind.JSDocFunctionType) {
@@ -23067,7 +23081,7 @@ namespace ts {
*
* @param node The signature to check
*/
function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration | MethodSignature): Type {
function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration | MethodSignature, returnTypeNode: TypeNode): Type {
// As part of our emit for an async function, we will need to emit the entity name of
// the return type annotation as an expression. To meet the necessary runtime semantics
// for __awaiter, we must also check that the type of the declaration (e.g. the static
@@ -23092,7 +23106,6 @@ namespace ts {
// then<U>(...): Promise<U>;
// }
//
const returnTypeNode = getEffectiveReturnTypeNode(node)!; // TODO: GH#18217
const returnType = getTypeFromTypeNode(returnTypeNode);
if (languageVersion >= ScriptTarget.ES2015) {
@@ -23502,15 +23515,12 @@ namespace ts {
const body = node.kind === SyntaxKind.MethodSignature ? undefined : node.body;
checkSourceElement(body);
const returnTypeNode = getEffectiveReturnTypeNode(node);
if ((functionFlags & FunctionFlags.Generator) === 0) { // Async function or normal function
const returnOrPromisedType = returnTypeNode && (functionFlags & FunctionFlags.Async
? checkAsyncFunctionReturnType(node) // Async function
: getTypeFromTypeNode(returnTypeNode)); // normal function
const returnOrPromisedType = getReturnOrPromisedType(node, functionFlags);
checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType);
}
if (produceDiagnostics && !returnTypeNode) {
if (produceDiagnostics && !getEffectiveReturnTypeNode(node)) {
// Report an implicit any error if there is no body, no explicit return type, and node is not a private method
// in an ambient context
if (noImplicitAny && nodeIsMissing(body) && !isPrivateWithinAmbient(node)) {
@@ -23523,6 +23533,13 @@ namespace ts {
// yielded values. The only way to trigger these errors is to try checking its return type.
getReturnTypeOfSignature(getSignatureFromDeclaration(node));
}
// A js function declaration can have a @type tag instead of a return type node, but that type must have a call signature
if (isInJavaScriptFile(node)) {
const typeTag = getJSDocTypeTag(node);
if (typeTag && typeTag.typeExpression && !getSignaturesOfType(getTypeFromTypeNode(typeTag.typeExpression), SignatureKind.Call).length) {
error(typeTag, Diagnostics.The_type_of_a_function_declaration_must_be_callable);
}
}
}
}
@@ -24785,7 +24802,7 @@ namespace ts {
error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class);
}
}
else if (getEffectiveReturnTypeNode(func) || isGetAccessorWithAnnotatedSetAccessor(func)) {
else if (getEffectiveReturnTypeNode(func) || isGetAccessorWithAnnotatedSetAccessor(func) || getReturnTypeOfTypeTag(func)) {
if (functionFlags & FunctionFlags.Async) { // Async function
const promisedType = getPromisedTypeOfPromise(returnType);
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);

View File

@@ -4021,6 +4021,10 @@
"category": "Error",
"code": 8029
},
"The type of a function declaration must be callable.": {
"category": "Error",
"code": 8030
},
"Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": {
"category": "Error",
"code": 9002