Properly catch and error on circular function return types

This commit is contained in:
Anders Hejlsberg 2018-08-03 14:03:30 -07:00
parent 4bc7f1570b
commit e4443bb993
3 changed files with 62 additions and 53 deletions

View File

@ -7532,9 +7532,10 @@ namespace ts {
getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
: undefined;
const typeParameters = classType ? classType.localTypeParameters : getTypeParametersFromDeclaration(declaration);
const returnType = getSignatureReturnTypeFromDeclaration(declaration, isJSConstructSignature, classType);
const hasRestLikeParameter = hasRestParameter(declaration) || isInJavaScriptFile(declaration) && maybeAddJsSyntheticRestParameter(declaration, parameters);
links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters, returnType, /*resolvedTypePredicate*/ undefined, minArgumentCount, hasRestLikeParameter, hasLiteralTypes);
links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters,
/*resolvedReturnType*/ undefined, /*resolvedTypePredicate*/ undefined,
minArgumentCount, hasRestLikeParameter, hasLiteralTypes);
}
return links.resolvedSignature;
}
@ -7564,34 +7565,6 @@ namespace ts {
return true;
}
function getSignatureReturnTypeFromDeclaration(declaration: SignatureDeclaration | JSDocSignature, isJSConstructSignature: boolean, classType: Type | undefined) {
if (isJSConstructSignature) {
return getTypeFromTypeNode((declaration.parameters[0] as ParameterDeclaration).type!); // TODO: GH#18217
}
else if (classType) {
return classType;
}
const typeNode = getEffectiveReturnTypeNode(declaration);
if (typeNode) {
return getTypeFromTypeNode(typeNode);
}
// TypeScript 1.0 spec (April 2014):
// If only one accessor includes a type annotation, the other behaves as if it had the same type annotation.
if (declaration.kind === SyntaxKind.GetAccessor && !hasNonBindableDynamicName(declaration)) {
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);
@ -7696,34 +7669,61 @@ namespace ts {
if (!pushTypeResolution(signature, TypeSystemPropertyName.ResolvedReturnType)) {
return errorType;
}
let type: Type;
if (signature.target) {
type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper!);
}
else if (signature.unionSignatures) {
type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype);
}
else {
type = getReturnTypeFromBody(<FunctionLikeDeclaration>signature.declaration);
}
let type = signature.target ? instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper!) :
signature.unionSignatures ? getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype) :
getReturnTypeFromAnnotationOrBody(signature.declaration!);
if (!popTypeResolution()) {
type = anyType;
if (noImplicitAny) {
const declaration = <Declaration>signature.declaration;
const name = getNameOfDeclaration(declaration);
if (name) {
error(name, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, declarationNameToString(name));
if (signature.declaration) {
const typeNode = getEffectiveReturnTypeNode(signature.declaration);
if (typeNode) {
error(typeNode, Diagnostics.Return_type_annotation_circularly_references_itself);
}
else {
error(declaration, Diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions);
else if (noImplicitAny) {
const declaration = <Declaration>signature.declaration;
const name = getNameOfDeclaration(declaration);
if (name) {
error(name, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, declarationNameToString(name));
}
else {
error(declaration, Diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions);
}
}
}
type = anyType;
}
signature.resolvedReturnType = type;
}
return signature.resolvedReturnType;
}
function getReturnTypeFromAnnotationOrBody(declaration: SignatureDeclaration | JSDocSignature) {
if (declaration.kind === SyntaxKind.Constructor) {
return getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
}
if (isJSDocConstructSignature(declaration)) {
return getTypeFromTypeNode((declaration.parameters[0] as ParameterDeclaration).type!); // TODO: GH#18217
}
const typeNode = getEffectiveReturnTypeNode(declaration);
if (typeNode) {
return getTypeFromTypeNode(typeNode);
}
if (declaration.kind === SyntaxKind.GetAccessor && !hasNonBindableDynamicName(declaration)) {
const setter = getDeclarationOfKind<AccessorDeclaration>(getSymbolOfNode(declaration), SyntaxKind.SetAccessor);
const setterType = getAnnotatedAccessorType(setter);
if (setterType) {
return setterType;
}
}
const typeFromTag = getReturnTypeOfTypeTag(declaration);
if (typeFromTag) {
return typeFromTag;
}
if (nodeIsMissing((<FunctionLikeDeclaration>declaration).body)) {
return anyType;
}
return getReturnTypeFromBody(<FunctionLikeDeclaration>declaration);
}
function isResolvingReturnTypeOfSignature(signature: Signature) {
return !signature.resolvedReturnType && findResolutionCycleStartIndex(signature, TypeSystemPropertyName.ResolvedReturnType) >= 0;
}
@ -20674,7 +20674,7 @@ namespace ts {
contextualSignature : instantiateSignature(contextualSignature, contextualMapper);
assignContextualParameterTypes(signature, instantiatedContextualSignature);
}
if (!getEffectiveReturnTypeNode(node) && !signature.resolvedReturnType) {
if (!getReturnOrPromisedType(node, getFunctionFlags(node)) && !signature.resolvedReturnType) {
const returnType = getReturnTypeFromBody(node, checkMode);
if (!signature.resolvedReturnType) {
signature.resolvedReturnType = returnType;
@ -22661,6 +22661,10 @@ namespace ts {
checkTypeAssignableTo(constraintType, keyofConstraintType, node.typeParameter.constraint);
}
function checkThisType(node: ThisTypeNode) {
getTypeFromThisTypeNode(node);
}
function checkTypeOperator(node: TypeOperatorNode) {
checkGrammarTypeOperatorNode(node);
checkSourceElement(node.type);
@ -26533,6 +26537,8 @@ namespace ts {
case SyntaxKind.OptionalType:
case SyntaxKind.RestType:
return checkSourceElement((<ParenthesizedTypeNode | OptionalTypeNode>node).type);
case SyntaxKind.ThisType:
return checkThisType(<ThisTypeNode>node);
case SyntaxKind.TypeOperator:
return checkTypeOperator(<TypeOperatorNode>node);
case SyntaxKind.ConditionalType:

View File

@ -2064,6 +2064,10 @@
"category": "Error",
"code": 2575
},
"Return type annotation circularly references itself.": {
"category": "Error",
"code": 2576
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600

View File

@ -3374,10 +3374,9 @@ namespace ts {
* JavaScript file, gets the return type annotation from JSDoc.
*/
export function getEffectiveReturnTypeNode(node: SignatureDeclaration | JSDocSignature): TypeNode | undefined {
if (isJSDocSignature(node)) {
return node.type && node.type.typeExpression && node.type.typeExpression.type;
}
return node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
return isJSDocSignature(node) ?
node.type && node.type.typeExpression && node.type.typeExpression.type :
node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
}
export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration> {