Skip to content

Commit e4443bb

Browse files
committed
Properly catch and error on circular function return types
1 parent 4bc7f15 commit e4443bb

3 files changed

Lines changed: 62 additions & 53 deletions

File tree

src/compiler/checker.ts

Lines changed: 55 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7532,9 +7532,10 @@ namespace ts {
75327532
getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
75337533
: undefined;
75347534
const typeParameters = classType ? classType.localTypeParameters : getTypeParametersFromDeclaration(declaration);
7535-
const returnType = getSignatureReturnTypeFromDeclaration(declaration, isJSConstructSignature, classType);
75367535
const hasRestLikeParameter = hasRestParameter(declaration) || isInJavaScriptFile(declaration) && maybeAddJsSyntheticRestParameter(declaration, parameters);
7537-
links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters, returnType, /*resolvedTypePredicate*/ undefined, minArgumentCount, hasRestLikeParameter, hasLiteralTypes);
7536+
links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters,
7537+
/*resolvedReturnType*/ undefined, /*resolvedTypePredicate*/ undefined,
7538+
minArgumentCount, hasRestLikeParameter, hasLiteralTypes);
75387539
}
75397540
return links.resolvedSignature;
75407541
}
@@ -7564,34 +7565,6 @@ namespace ts {
75647565
return true;
75657566
}
75667567

7567-
function getSignatureReturnTypeFromDeclaration(declaration: SignatureDeclaration | JSDocSignature, isJSConstructSignature: boolean, classType: Type | undefined) {
7568-
if (isJSConstructSignature) {
7569-
return getTypeFromTypeNode((declaration.parameters[0] as ParameterDeclaration).type!); // TODO: GH#18217
7570-
}
7571-
else if (classType) {
7572-
return classType;
7573-
}
7574-
7575-
const typeNode = getEffectiveReturnTypeNode(declaration);
7576-
if (typeNode) {
7577-
return getTypeFromTypeNode(typeNode);
7578-
}
7579-
7580-
// TypeScript 1.0 spec (April 2014):
7581-
// If only one accessor includes a type annotation, the other behaves as if it had the same type annotation.
7582-
if (declaration.kind === SyntaxKind.GetAccessor && !hasNonBindableDynamicName(declaration)) {
7583-
const setter = getDeclarationOfKind<AccessorDeclaration>(getSymbolOfNode(declaration), SyntaxKind.SetAccessor);
7584-
return getAnnotatedAccessorType(setter);
7585-
}
7586-
const typeFromTag = getReturnTypeOfTypeTag(declaration);
7587-
if (typeFromTag) {
7588-
return typeFromTag;
7589-
}
7590-
if (nodeIsMissing((<FunctionLikeDeclaration>declaration).body)) {
7591-
return anyType;
7592-
}
7593-
}
7594-
75957568
function getReturnTypeOfTypeTag(node: SignatureDeclaration | JSDocSignature) {
75967569
const typeTag = isInJavaScriptFile(node) ? getJSDocTypeTag(node) : undefined;
75977570
const signatures = typeTag && typeTag.typeExpression && getSignaturesOfType(getTypeFromTypeNode(typeTag.typeExpression), SignatureKind.Call);
@@ -7696,34 +7669,61 @@ namespace ts {
76967669
if (!pushTypeResolution(signature, TypeSystemPropertyName.ResolvedReturnType)) {
76977670
return errorType;
76987671
}
7699-
let type: Type;
7700-
if (signature.target) {
7701-
type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper!);
7702-
}
7703-
else if (signature.unionSignatures) {
7704-
type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype);
7705-
}
7706-
else {
7707-
type = getReturnTypeFromBody(<FunctionLikeDeclaration>signature.declaration);
7708-
}
7672+
let type = signature.target ? instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper!) :
7673+
signature.unionSignatures ? getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype) :
7674+
getReturnTypeFromAnnotationOrBody(signature.declaration!);
77097675
if (!popTypeResolution()) {
7710-
type = anyType;
7711-
if (noImplicitAny) {
7712-
const declaration = <Declaration>signature.declaration;
7713-
const name = getNameOfDeclaration(declaration);
7714-
if (name) {
7715-
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));
7676+
if (signature.declaration) {
7677+
const typeNode = getEffectiveReturnTypeNode(signature.declaration);
7678+
if (typeNode) {
7679+
error(typeNode, Diagnostics.Return_type_annotation_circularly_references_itself);
77167680
}
7717-
else {
7718-
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);
7681+
else if (noImplicitAny) {
7682+
const declaration = <Declaration>signature.declaration;
7683+
const name = getNameOfDeclaration(declaration);
7684+
if (name) {
7685+
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));
7686+
}
7687+
else {
7688+
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);
7689+
}
77197690
}
77207691
}
7692+
type = anyType;
77217693
}
77227694
signature.resolvedReturnType = type;
77237695
}
77247696
return signature.resolvedReturnType;
77257697
}
77267698

7699+
function getReturnTypeFromAnnotationOrBody(declaration: SignatureDeclaration | JSDocSignature) {
7700+
if (declaration.kind === SyntaxKind.Constructor) {
7701+
return getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
7702+
}
7703+
if (isJSDocConstructSignature(declaration)) {
7704+
return getTypeFromTypeNode((declaration.parameters[0] as ParameterDeclaration).type!); // TODO: GH#18217
7705+
}
7706+
const typeNode = getEffectiveReturnTypeNode(declaration);
7707+
if (typeNode) {
7708+
return getTypeFromTypeNode(typeNode);
7709+
}
7710+
if (declaration.kind === SyntaxKind.GetAccessor && !hasNonBindableDynamicName(declaration)) {
7711+
const setter = getDeclarationOfKind<AccessorDeclaration>(getSymbolOfNode(declaration), SyntaxKind.SetAccessor);
7712+
const setterType = getAnnotatedAccessorType(setter);
7713+
if (setterType) {
7714+
return setterType;
7715+
}
7716+
}
7717+
const typeFromTag = getReturnTypeOfTypeTag(declaration);
7718+
if (typeFromTag) {
7719+
return typeFromTag;
7720+
}
7721+
if (nodeIsMissing((<FunctionLikeDeclaration>declaration).body)) {
7722+
return anyType;
7723+
}
7724+
return getReturnTypeFromBody(<FunctionLikeDeclaration>declaration);
7725+
}
7726+
77277727
function isResolvingReturnTypeOfSignature(signature: Signature) {
77287728
return !signature.resolvedReturnType && findResolutionCycleStartIndex(signature, TypeSystemPropertyName.ResolvedReturnType) >= 0;
77297729
}
@@ -20674,7 +20674,7 @@ namespace ts {
2067420674
contextualSignature : instantiateSignature(contextualSignature, contextualMapper);
2067520675
assignContextualParameterTypes(signature, instantiatedContextualSignature);
2067620676
}
20677-
if (!getEffectiveReturnTypeNode(node) && !signature.resolvedReturnType) {
20677+
if (!getReturnOrPromisedType(node, getFunctionFlags(node)) && !signature.resolvedReturnType) {
2067820678
const returnType = getReturnTypeFromBody(node, checkMode);
2067920679
if (!signature.resolvedReturnType) {
2068020680
signature.resolvedReturnType = returnType;
@@ -22661,6 +22661,10 @@ namespace ts {
2266122661
checkTypeAssignableTo(constraintType, keyofConstraintType, node.typeParameter.constraint);
2266222662
}
2266322663

22664+
function checkThisType(node: ThisTypeNode) {
22665+
getTypeFromThisTypeNode(node);
22666+
}
22667+
2266422668
function checkTypeOperator(node: TypeOperatorNode) {
2266522669
checkGrammarTypeOperatorNode(node);
2266622670
checkSourceElement(node.type);
@@ -26533,6 +26537,8 @@ namespace ts {
2653326537
case SyntaxKind.OptionalType:
2653426538
case SyntaxKind.RestType:
2653526539
return checkSourceElement((<ParenthesizedTypeNode | OptionalTypeNode>node).type);
26540+
case SyntaxKind.ThisType:
26541+
return checkThisType(<ThisTypeNode>node);
2653626542
case SyntaxKind.TypeOperator:
2653726543
return checkTypeOperator(<TypeOperatorNode>node);
2653826544
case SyntaxKind.ConditionalType:

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,6 +2064,10 @@
20642064
"category": "Error",
20652065
"code": 2575
20662066
},
2067+
"Return type annotation circularly references itself.": {
2068+
"category": "Error",
2069+
"code": 2576
2070+
},
20672071
"JSX element attributes type '{0}' may not be a union type.": {
20682072
"category": "Error",
20692073
"code": 2600

src/compiler/utilities.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3374,10 +3374,9 @@ namespace ts {
33743374
* JavaScript file, gets the return type annotation from JSDoc.
33753375
*/
33763376
export function getEffectiveReturnTypeNode(node: SignatureDeclaration | JSDocSignature): TypeNode | undefined {
3377-
if (isJSDocSignature(node)) {
3378-
return node.type && node.type.typeExpression && node.type.typeExpression.type;
3379-
}
3380-
return node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
3377+
return isJSDocSignature(node) ?
3378+
node.type && node.type.typeExpression && node.type.typeExpression.type :
3379+
node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
33813380
}
33823381

33833382
export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration> {

0 commit comments

Comments
 (0)