diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 958ceae0395..890f8f4beb8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7577,7 +7577,19 @@ namespace ts { constraintDepth++; let result = computeBaseConstraint(getSimplifiedType(t)); constraintDepth--; - if (!popTypeResolution() || nonTerminating) { + if (!popTypeResolution()) { + if (t.flags & TypeFlags.TypeParameter) { + const errorNode = getConstraintDeclaration(t); + if (errorNode) { + const diagnostic = error(errorNode, Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(t)); + if (currentNode && !isNodeDescendantOf(errorNode, currentNode) && !isNodeDescendantOf(currentNode, errorNode)) { + addRelatedInfo(diagnostic, createDiagnosticForNode(currentNode, Diagnostics.Circularity_originates_in_type_at_this_location)); + } + } + } + result = circularConstraintType; + } + if (nonTerminating) { result = circularConstraintType; } t.immediateBaseConstraint = result || noConstraintType; @@ -23475,9 +23487,8 @@ namespace ts { checkSourceElement(node.constraint); checkSourceElement(node.default); const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node)); - if (!hasNonCircularBaseConstraint(typeParameter)) { - error(getEffectiveConstraintOfTypeParameter(node), Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(typeParameter)); - } + // Resolve base constraint to reveal circularity errors + getBaseConstraintOfType(typeParameter); if (!hasNonCircularTypeParameterDefault(typeParameter)) { error(node.default, Diagnostics.Type_parameter_0_has_a_circular_default, typeToString(typeParameter)); } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 09b0f721292..5ae3a44b4b9 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2589,6 +2589,10 @@ "category": "Error", "code": 2750 }, + "Circularity originates in type at this location.": { + "category": "Error", + "code": 2751 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error",