Skip to content

Commit

Permalink
Expand constraint suggestion related span and add quick fix (#49481)
Browse files Browse the repository at this point in the history
* Expand constraint suggestion related span and add quick fix

* Remove circular constraint suggestions

* Add error code

* Style feedback and new error code in quickfix
  • Loading branch information
weswigham committed Jun 15, 2022
1 parent eb4b8a4 commit ba38fe1
Show file tree
Hide file tree
Showing 80 changed files with 336 additions and 13 deletions.
9 changes: 7 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18806,8 +18806,13 @@ namespace ts {
return;
}
reportRelationError(headMessage, source, target);
if (strictNullChecks && source.flags & TypeFlags.TypeVariable && source.symbol?.declarations?.[0] && !getConstraintOfType(source as TypeVariable) && isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) {
associateRelatedInfo(createDiagnosticForNode(source.symbol.declarations[0], Diagnostics.This_type_parameter_probably_needs_an_extends_object_constraint));
if (source.flags & TypeFlags.TypeParameter && source.symbol?.declarations?.[0] && !getConstraintOfType(source as TypeVariable)) {
const syntheticParam = cloneTypeParameter(source as TypeParameter);
syntheticParam.constraint = instantiateType(target, makeUnaryTypeMapper(source, syntheticParam));
if (hasNonCircularBaseConstraint(syntheticParam)) {
const targetConstraintString = typeToString(target, source.symbol.declarations[0]);
associateRelatedInfo(createDiagnosticForNode(source.symbol.declarations[0], Diagnostics.This_type_parameter_might_need_an_extends_0_constraint, targetConstraintString));
}
}
}

Expand Down
10 changes: 9 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1530,7 +1530,7 @@
"category": "Error",
"code": 2207
},
"This type parameter probably needs an `extends object` constraint.": {
"This type parameter might need an `extends {0}` constraint.": {
"category": "Error",
"code": 2208
},
Expand All @@ -1543,6 +1543,14 @@
"category": "Error",
"code": 2210
},
"Add `extends` constraint.": {
"category": "Message",
"code": 2211
},
"Add `extends` constraint to all type parameters": {
"category": "Message",
"code": 2212
},

"Duplicate identifier '{0}'.": {
"category": "Error",
Expand Down
65 changes: 65 additions & 0 deletions src/services/codefixes/fixAddMissingConstraint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* @internal */
namespace ts.codefix {
const fixId = "addMissingConstraint";
const errorCodes = [
// We want errors this could be attached to:
// Diagnostics.This_type_parameter_probably_needs_an_extends_0_constraint
Diagnostics.Type_0_is_not_comparable_to_type_1.code,
Diagnostics.Type_0_is_not_assignable_to_type_1_Two_different_types_with_this_name_exist_but_they_are_unrelated.code,
Diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_types_of_the_target_s_properties.code,
Diagnostics.Type_0_is_not_assignable_to_type_1.code,
Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_types_of_the_target_s_properties.code,
Diagnostics.Property_0_is_incompatible_with_index_signature.code,
Diagnostics.Property_0_in_type_1_is_not_assignable_to_type_2.code,
Diagnostics.Type_0_does_not_satisfy_the_constraint_1.code,
];
registerCodeFix({
errorCodes,
getCodeActions(context) {
const { sourceFile, span, program } = context;
const related = getDiagnosticRelatedInfo(program, sourceFile, span);
if (!related) {
return;
}
const changes = textChanges.ChangeTracker.with(context, t => addMissingConstraint(t, related));
return [createCodeFixAction(fixId, changes, Diagnostics.Add_extends_constraint, fixId, Diagnostics.Add_extends_constraint_to_all_type_parameters)];
},
fixIds: [fixId],
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
const info = getDiagnosticRelatedInfo(context.program, context.sourceFile, diag);
if (!info) return;
return addMissingConstraint(changes, info);
}),
});

function getDiagnosticRelatedInfo(program: Program, sourceFile: SourceFile, span: TextSpan) {
const diag = find(program.getSemanticDiagnostics(sourceFile), diag => diag.start === span.start && diag.length === span.length);
if (!diag || !diag.relatedInformation) return;
const related = find(diag.relatedInformation, related => related.code === Diagnostics.This_type_parameter_might_need_an_extends_0_constraint.code);
if (!related) return;
return related;
}

function addMissingConstraint(changes: textChanges.ChangeTracker, related: DiagnosticRelatedInformation): void {
let decl = findAncestorMatchingSpan(related.file!, related as TextSpan);
if (!decl) return;
if (isIdentifier(decl) && isTypeParameterDeclaration(decl.parent)) {
decl = decl.parent;
}
if (!isTypeParameterDeclaration(decl) || isMappedTypeNode(decl.parent)) return; // should only issue fix on type parameters written using `extends`
const newConstraint = flattenDiagnosticMessageText(related.messageText, "\n", 0).match(/`extends (.*)`/);
if (!newConstraint) return;
const newConstraintText = newConstraint[1];

changes.insertText(related.file!, related.start! + related.length!, ` extends ${newConstraintText}`);
}

function findAncestorMatchingSpan(sourceFile: SourceFile, span: TextSpan): Node {
let token = getTokenAtPosition(sourceFile, span.start);
const end = textSpanEnd(span);
while (token.end < end) {
token = token.parent;
}
return token;
}
}
1 change: 1 addition & 0 deletions src/services/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"codefixes/convertLiteralTypeToMappedType.ts",
"codefixes/fixClassIncorrectlyImplementsInterface.ts",
"codefixes/importFixes.ts",
"codefixes/fixAddMissingConstraint.ts",
"codefixes/fixOverrideModifier.ts",
"codefixes/fixNoPropertyAccessFromIndexSignature.ts",
"codefixes/fixImplicitThis.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,27 +114,31 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Type '(x: number) => number[]' is not assignable to type '<T>(x: T) => T[]'.
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'number'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures3.ts:45:9: This type parameter might need an `extends number` constraint.
var b2: <T>(x: T) => string[];
a2 = b2; // ok
b2 = a2; // ok
~~
!!! error TS2322: Type '(x: number) => string[]' is not assignable to type '<T>(x: T) => string[]'.
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'number'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures3.ts:48:10: This type parameter might need an `extends number` constraint.
var b3: <T>(x: T) => T;
a3 = b3; // ok
b3 = a3; // ok
~~
!!! error TS2322: Type '(x: number) => void' is not assignable to type '<T>(x: T) => T'.
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'number'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures3.ts:51:10: This type parameter might need an `extends number` constraint.
var b4: <T, U>(x: T, y: U) => T;
a4 = b4; // ok
b4 = a4; // ok
~~
!!! error TS2322: Type '(x: string, y: number) => string' is not assignable to type '<T, U>(x: T, y: U) => T'.
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'string'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures3.ts:54:10: This type parameter might need an `extends string` constraint.
var b5: <T, U>(x: (arg: T) => U) => T;
a5 = b5; // ok
b5 = a5; // ok
Expand Down Expand Up @@ -227,6 +231,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Type '{ a: T; b: T; }' is not assignable to type '{ a: string; b: number; }'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'string'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures3.ts:84:11: This type parameter might need an `extends string` constraint.
var b15: <T>(x: T) => T[];
a15 = b15; // ok
b15 = a15; // ok
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Type '(x: number) => string[]' is not assignable to type '<T, U>(x: T) => U[]'.
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'number'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures4.ts:43:18: This type parameter might need an `extends number` constraint.

var b7: <T extends Base, U extends Derived, V extends Derived2>(x: (arg: T) => U) => (r: T) => V;
a7 = b7;
Expand Down Expand Up @@ -181,6 +182,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Type '{ a: T; b: T; }' is not assignable to type '{ a: string; b: number; }'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'string'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures4.ts:68:19: This type parameter might need an `extends string` constraint.

var b15a: <T extends Base>(x: { a: T; b: T }) => number;
a15 = b15a;
Expand Down Expand Up @@ -223,6 +225,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Type '<T>(x: T) => T[]' is not assignable to type '<T>(x: T) => string[]'.
!!! error TS2322: Type 'T[]' is not assignable to type 'string[]'.
!!! error TS2322: Type 'T' is not assignable to type 'string'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures4.ts:88:18: This type parameter might need an `extends string` constraint.

// target type has generic call signature
var a3: <T>(x: T) => string[];
Expand All @@ -232,6 +235,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Type '<T>(x: T) => T[]' is not assignable to type '<T>(x: T) => string[]'.
!!! error TS2322: Type 'T[]' is not assignable to type 'string[]'.
!!! error TS2322: Type 'T' is not assignable to type 'string'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures4.ts:93:18: This type parameter might need an `extends string` constraint.
b3 = a3;
~~
!!! error TS2322: Type '<T>(x: T) => string[]' is not assignable to type '<T>(x: T) => T[]'.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Types of property 'foo' are incompatible.
!!! error TS2322: Type 'U' is not assignable to type 'T'.
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'U'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures5.ts:50:14: This type parameter might need an `extends T` constraint.
var b15: <U, V>(x: { a: U; b: V; }) => U[];
a15 = b15; // ok, T = U, T = V
b15 = a15; // ok
Expand All @@ -94,6 +95,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Types of property 'b' are incompatible.
!!! error TS2322: Type 'V' is not assignable to type 'U'.
!!! error TS2322: 'U' could be instantiated with an arbitrary type which could be unrelated to 'V'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures5.ts:53:14: This type parameter might need an `extends U` constraint.
var b16: <T>(x: { a: T; b: T }) => T[];
a15 = b16; // ok
b15 = a16; // ok
Expand All @@ -103,6 +105,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Type '{ a: U; b: V; }' is not assignable to type '{ a: Base; b: Base; }'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Type 'U' is not assignable to type 'Base'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures5.ts:53:11: This type parameter might need an `extends Base` constraint.
var b17: <T>(x: (a: T) => T) => T[];
a17 = b17; // ok
b17 = a17; // ok
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Types of property 'foo' are incompatible.
!!! error TS2322: Type 'U' is not assignable to type 'T'.
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'U'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures6.ts:37:14: This type parameter might need an `extends T` constraint.
var b16: <T>(x: { a: T; b: T }) => T[];
x.a16 = b16;
b16 = x.a16;
Expand All @@ -73,4 +74,5 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type '{ a: T; b: T; }' is not assignable to type '{ a: Base; b: Base; }'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'Base'.
!!! error TS2322: Type 'T' is not assignable to type 'Base'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures6.ts:40:11: This type parameter might need an `extends Base` constraint.
Original file line number Diff line number Diff line change
Expand Up @@ -114,27 +114,31 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Type 'new (x: number) => number[]' is not assignable to type 'new <T>(x: T) => T[]'.
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'number'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithConstructSignatures3.ts:45:13: This type parameter might need an `extends number` constraint.
var b2: new <T>(x: T) => string[];
a2 = b2; // ok
b2 = a2; // ok
~~
!!! error TS2322: Type 'new (x: number) => string[]' is not assignable to type 'new <T>(x: T) => string[]'.
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'number'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithConstructSignatures3.ts:48:14: This type parameter might need an `extends number` constraint.
var b3: new <T>(x: T) => T;
a3 = b3; // ok
b3 = a3; // ok
~~
!!! error TS2322: Type 'new (x: number) => void' is not assignable to type 'new <T>(x: T) => T'.
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'number'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithConstructSignatures3.ts:51:14: This type parameter might need an `extends number` constraint.
var b4: new <T, U>(x: T, y: U) => T;
a4 = b4; // ok
b4 = a4; // ok
~~
!!! error TS2322: Type 'new (x: string, y: number) => string' is not assignable to type 'new <T, U>(x: T, y: U) => T'.
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'string'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithConstructSignatures3.ts:54:14: This type parameter might need an `extends string` constraint.
var b5: new <T, U>(x: (arg: T) => U) => T;
a5 = b5; // ok
b5 = a5; // ok
Expand Down Expand Up @@ -227,6 +231,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
!!! error TS2322: Type '{ a: T; b: T; }' is not assignable to type '{ a: string; b: number; }'.
!!! error TS2322: Types of property 'a' are incompatible.
!!! error TS2322: Type 'T' is not assignable to type 'string'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithConstructSignatures3.ts:84:15: This type parameter might need an `extends string` constraint.
var b15: new <T>(x: T) => T[];
a15 = b15; // ok
b15 = a15; // ok
Expand Down

0 comments on commit ba38fe1

Please sign in to comment.