mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-17 11:24:29 -05:00
Offer per-member diagnostics for incorrectly implemented inherited members (#21036)
* Offer per-member diagnostics for incorrectly implemented inherited members on classes * Revise error message, make containingChain a thunk * Fix typo in comment
This commit is contained in:
@@ -9169,7 +9169,7 @@ namespace ts {
|
||||
return isTypeComparableTo(type1, type2) || isTypeComparableTo(type2, type1);
|
||||
}
|
||||
|
||||
function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean {
|
||||
function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined): boolean {
|
||||
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage, containingMessageChain);
|
||||
}
|
||||
|
||||
@@ -9177,7 +9177,7 @@ namespace ts {
|
||||
* This is *not* a bi-directional relationship.
|
||||
* If one needs to check both directions for comparability, use a second call to this function or 'isTypeComparableTo'.
|
||||
*/
|
||||
function checkTypeComparableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean {
|
||||
function checkTypeComparableTo(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined): boolean {
|
||||
return checkTypeRelatedTo(source, target, comparableRelation, errorNode, headMessage, containingMessageChain);
|
||||
}
|
||||
|
||||
@@ -9511,7 +9511,7 @@ namespace ts {
|
||||
relation: Map<RelationComparisonResult>,
|
||||
errorNode: Node,
|
||||
headMessage?: DiagnosticMessage,
|
||||
containingMessageChain?: DiagnosticMessageChain): boolean {
|
||||
containingMessageChain?: () => DiagnosticMessageChain | undefined): boolean {
|
||||
|
||||
let errorInfo: DiagnosticMessageChain;
|
||||
let maybeKeys: string[];
|
||||
@@ -9531,7 +9531,10 @@ namespace ts {
|
||||
}
|
||||
else if (errorInfo) {
|
||||
if (containingMessageChain) {
|
||||
errorInfo = concatenateDiagnosticMessageChains(containingMessageChain, errorInfo);
|
||||
const chain = containingMessageChain();
|
||||
if (chain) {
|
||||
errorInfo = concatenateDiagnosticMessageChains(chain, errorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics.add(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo));
|
||||
@@ -16675,7 +16678,7 @@ namespace ts {
|
||||
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
|
||||
if (!constraint) continue;
|
||||
|
||||
const errorInfo = reportErrors && headMessage && chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
|
||||
const errorInfo = reportErrors && headMessage && (() => chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Type_0_does_not_satisfy_the_constraint_1));
|
||||
const typeArgumentHeadMessage = headMessage || Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
|
||||
if (!mapper) {
|
||||
mapper = createTypeMapper(typeParameters, typeArgumentTypes);
|
||||
@@ -19697,7 +19700,7 @@ namespace ts {
|
||||
Diagnostics.A_type_predicate_cannot_reference_a_rest_parameter);
|
||||
}
|
||||
else {
|
||||
const leadingError = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.A_type_predicate_s_type_must_be_assignable_to_its_parameter_s_type);
|
||||
const leadingError = () => chainDiagnosticMessages(/*details*/ undefined, Diagnostics.A_type_predicate_s_type_must_be_assignable_to_its_parameter_s_type);
|
||||
checkTypeAssignableTo(typePredicate.type,
|
||||
getTypeOfNode(parent.parameters[typePredicate.parameterIndex]),
|
||||
node.type,
|
||||
@@ -20984,7 +20987,7 @@ namespace ts {
|
||||
expectedReturnType,
|
||||
node,
|
||||
headMessage,
|
||||
errorInfo);
|
||||
() => errorInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -22907,7 +22910,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(baseType, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1);
|
||||
const baseWithThis = getTypeWithThisArgument(baseType, type.thisType);
|
||||
if (!checkTypeAssignableTo(typeWithThis, baseWithThis, /*errorNode*/ undefined)) {
|
||||
issueMemberSpecificError(node, typeWithThis, baseWithThis, Diagnostics.Class_0_incorrectly_extends_base_class_1);
|
||||
}
|
||||
checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node,
|
||||
Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1);
|
||||
if (baseConstructorType.flags & TypeFlags.TypeVariable && !isMixinConstructorType(staticType)) {
|
||||
@@ -22939,12 +22945,13 @@ namespace ts {
|
||||
const t = getTypeFromTypeNode(typeRefNode);
|
||||
if (t !== unknownType) {
|
||||
if (isValidBaseType(t)) {
|
||||
checkTypeAssignableTo(typeWithThis,
|
||||
getTypeWithThisArgument(t, type.thisType),
|
||||
node.name || node,
|
||||
t.symbol && t.symbol.flags & SymbolFlags.Class ?
|
||||
Diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass :
|
||||
Diagnostics.Class_0_incorrectly_implements_interface_1);
|
||||
const genericDiag = t.symbol && t.symbol.flags & SymbolFlags.Class ?
|
||||
Diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass :
|
||||
Diagnostics.Class_0_incorrectly_implements_interface_1;
|
||||
const baseWithThis = getTypeWithThisArgument(t, type.thisType);
|
||||
if (!checkTypeAssignableTo(typeWithThis, baseWithThis, /*errorNode*/ undefined)) {
|
||||
issueMemberSpecificError(node, typeWithThis, baseWithThis, genericDiag);
|
||||
}
|
||||
}
|
||||
else {
|
||||
error(typeRefNode, Diagnostics.A_class_may_only_implement_another_class_or_interface);
|
||||
@@ -22961,6 +22968,37 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function issueMemberSpecificError(node: ClassLikeDeclaration, typeWithThis: Type, baseWithThis: Type, broadDiag: DiagnosticMessage) {
|
||||
// iterate over all implemented properties and issue errors on each one which isn't compatible, rather than the class as a whole, if possible
|
||||
let issuedMemberError = false;
|
||||
for (const member of node.members) {
|
||||
if (hasStaticModifier(member)) {
|
||||
continue;
|
||||
}
|
||||
const declaredProp = member.name && getSymbolAtLocation(member.name) || getSymbolAtLocation(member);
|
||||
if (declaredProp) {
|
||||
const prop = getPropertyOfType(typeWithThis, declaredProp.escapedName);
|
||||
const baseProp = getPropertyOfType(baseWithThis, declaredProp.escapedName);
|
||||
if (prop && baseProp) {
|
||||
const rootChain = () => chainDiagnosticMessages(
|
||||
/*details*/ undefined,
|
||||
Diagnostics.Property_0_in_type_1_is_not_assignable_to_the_same_property_in_base_type_2,
|
||||
unescapeLeadingUnderscores(declaredProp.escapedName),
|
||||
typeToString(typeWithThis),
|
||||
typeToString(getTypeOfSymbol(baseProp))
|
||||
);
|
||||
if (!checkTypeAssignableTo(getTypeOfSymbol(prop), getTypeOfSymbol(baseProp), member.name || member, /*message*/ undefined, rootChain)) {
|
||||
issuedMemberError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!issuedMemberError) {
|
||||
// check again with diagnostics to generate a less-specific error
|
||||
checkTypeAssignableTo(typeWithThis, baseWithThis, node.name || node, broadDiag);
|
||||
}
|
||||
}
|
||||
|
||||
function checkBaseTypeAccessibility(type: Type, node: ExpressionWithTypeArguments) {
|
||||
const signatures = getSignaturesOfType(type, SignatureKind.Construct);
|
||||
if (signatures.length) {
|
||||
|
||||
@@ -1404,6 +1404,10 @@
|
||||
"category": "Error",
|
||||
"code": 2415
|
||||
},
|
||||
"Property '{0}' in type '{1}' is not assignable to the same property in base type '{2}'.": {
|
||||
"category": "Error",
|
||||
"code": 2416
|
||||
},
|
||||
"Class static side '{0}' incorrectly extends base class static side '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 2417
|
||||
|
||||
Reference in New Issue
Block a user