Skip to content

Commit ba38fe1

Browse files
authored
Expand constraint suggestion related span and add quick fix (microsoft#49481)
* Expand constraint suggestion related span and add quick fix * Remove circular constraint suggestions * Add error code * Style feedback and new error code in quickfix
1 parent eb4b8a4 commit ba38fe1

File tree

80 files changed

+336
-13
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+336
-13
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18806,8 +18806,13 @@ namespace ts {
1880618806
return;
1880718807
}
1880818808
reportRelationError(headMessage, source, target);
18809-
if (strictNullChecks && source.flags & TypeFlags.TypeVariable && source.symbol?.declarations?.[0] && !getConstraintOfType(source as TypeVariable) && isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) {
18810-
associateRelatedInfo(createDiagnosticForNode(source.symbol.declarations[0], Diagnostics.This_type_parameter_probably_needs_an_extends_object_constraint));
18809+
if (source.flags & TypeFlags.TypeParameter && source.symbol?.declarations?.[0] && !getConstraintOfType(source as TypeVariable)) {
18810+
const syntheticParam = cloneTypeParameter(source as TypeParameter);
18811+
syntheticParam.constraint = instantiateType(target, makeUnaryTypeMapper(source, syntheticParam));
18812+
if (hasNonCircularBaseConstraint(syntheticParam)) {
18813+
const targetConstraintString = typeToString(target, source.symbol.declarations[0]);
18814+
associateRelatedInfo(createDiagnosticForNode(source.symbol.declarations[0], Diagnostics.This_type_parameter_might_need_an_extends_0_constraint, targetConstraintString));
18815+
}
1881118816
}
1881218817
}
1881318818

src/compiler/diagnosticMessages.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1530,7 +1530,7 @@
15301530
"category": "Error",
15311531
"code": 2207
15321532
},
1533-
"This type parameter probably needs an `extends object` constraint.": {
1533+
"This type parameter might need an `extends {0}` constraint.": {
15341534
"category": "Error",
15351535
"code": 2208
15361536
},
@@ -1543,6 +1543,14 @@
15431543
"category": "Error",
15441544
"code": 2210
15451545
},
1546+
"Add `extends` constraint.": {
1547+
"category": "Message",
1548+
"code": 2211
1549+
},
1550+
"Add `extends` constraint to all type parameters": {
1551+
"category": "Message",
1552+
"code": 2212
1553+
},
15461554

15471555
"Duplicate identifier '{0}'.": {
15481556
"category": "Error",
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/* @internal */
2+
namespace ts.codefix {
3+
const fixId = "addMissingConstraint";
4+
const errorCodes = [
5+
// We want errors this could be attached to:
6+
// Diagnostics.This_type_parameter_probably_needs_an_extends_0_constraint
7+
Diagnostics.Type_0_is_not_comparable_to_type_1.code,
8+
Diagnostics.Type_0_is_not_assignable_to_type_1_Two_different_types_with_this_name_exist_but_they_are_unrelated.code,
9+
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,
10+
Diagnostics.Type_0_is_not_assignable_to_type_1.code,
11+
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,
12+
Diagnostics.Property_0_is_incompatible_with_index_signature.code,
13+
Diagnostics.Property_0_in_type_1_is_not_assignable_to_type_2.code,
14+
Diagnostics.Type_0_does_not_satisfy_the_constraint_1.code,
15+
];
16+
registerCodeFix({
17+
errorCodes,
18+
getCodeActions(context) {
19+
const { sourceFile, span, program } = context;
20+
const related = getDiagnosticRelatedInfo(program, sourceFile, span);
21+
if (!related) {
22+
return;
23+
}
24+
const changes = textChanges.ChangeTracker.with(context, t => addMissingConstraint(t, related));
25+
return [createCodeFixAction(fixId, changes, Diagnostics.Add_extends_constraint, fixId, Diagnostics.Add_extends_constraint_to_all_type_parameters)];
26+
},
27+
fixIds: [fixId],
28+
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
29+
const info = getDiagnosticRelatedInfo(context.program, context.sourceFile, diag);
30+
if (!info) return;
31+
return addMissingConstraint(changes, info);
32+
}),
33+
});
34+
35+
function getDiagnosticRelatedInfo(program: Program, sourceFile: SourceFile, span: TextSpan) {
36+
const diag = find(program.getSemanticDiagnostics(sourceFile), diag => diag.start === span.start && diag.length === span.length);
37+
if (!diag || !diag.relatedInformation) return;
38+
const related = find(diag.relatedInformation, related => related.code === Diagnostics.This_type_parameter_might_need_an_extends_0_constraint.code);
39+
if (!related) return;
40+
return related;
41+
}
42+
43+
function addMissingConstraint(changes: textChanges.ChangeTracker, related: DiagnosticRelatedInformation): void {
44+
let decl = findAncestorMatchingSpan(related.file!, related as TextSpan);
45+
if (!decl) return;
46+
if (isIdentifier(decl) && isTypeParameterDeclaration(decl.parent)) {
47+
decl = decl.parent;
48+
}
49+
if (!isTypeParameterDeclaration(decl) || isMappedTypeNode(decl.parent)) return; // should only issue fix on type parameters written using `extends`
50+
const newConstraint = flattenDiagnosticMessageText(related.messageText, "\n", 0).match(/`extends (.*)`/);
51+
if (!newConstraint) return;
52+
const newConstraintText = newConstraint[1];
53+
54+
changes.insertText(related.file!, related.start! + related.length!, ` extends ${newConstraintText}`);
55+
}
56+
57+
function findAncestorMatchingSpan(sourceFile: SourceFile, span: TextSpan): Node {
58+
let token = getTokenAtPosition(sourceFile, span.start);
59+
const end = textSpanEnd(span);
60+
while (token.end < end) {
61+
token = token.parent;
62+
}
63+
return token;
64+
}
65+
}

src/services/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"codefixes/convertLiteralTypeToMappedType.ts",
6969
"codefixes/fixClassIncorrectlyImplementsInterface.ts",
7070
"codefixes/importFixes.ts",
71+
"codefixes/fixAddMissingConstraint.ts",
7172
"codefixes/fixOverrideModifier.ts",
7273
"codefixes/fixNoPropertyAccessFromIndexSignature.ts",
7374
"codefixes/fixImplicitThis.ts",

tests/baselines/reference/assignmentCompatWithCallSignatures3.errors.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,27 +114,31 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
114114
!!! error TS2322: Type '(x: number) => number[]' is not assignable to type '<T>(x: T) => T[]'.
115115
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
116116
!!! error TS2322: Type 'T' is not assignable to type 'number'.
117+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures3.ts:45:9: This type parameter might need an `extends number` constraint.
117118
var b2: <T>(x: T) => string[];
118119
a2 = b2; // ok
119120
b2 = a2; // ok
120121
~~
121122
!!! error TS2322: Type '(x: number) => string[]' is not assignable to type '<T>(x: T) => string[]'.
122123
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
123124
!!! error TS2322: Type 'T' is not assignable to type 'number'.
125+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures3.ts:48:10: This type parameter might need an `extends number` constraint.
124126
var b3: <T>(x: T) => T;
125127
a3 = b3; // ok
126128
b3 = a3; // ok
127129
~~
128130
!!! error TS2322: Type '(x: number) => void' is not assignable to type '<T>(x: T) => T'.
129131
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
130132
!!! error TS2322: Type 'T' is not assignable to type 'number'.
133+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures3.ts:51:10: This type parameter might need an `extends number` constraint.
131134
var b4: <T, U>(x: T, y: U) => T;
132135
a4 = b4; // ok
133136
b4 = a4; // ok
134137
~~
135138
!!! error TS2322: Type '(x: string, y: number) => string' is not assignable to type '<T, U>(x: T, y: U) => T'.
136139
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
137140
!!! error TS2322: Type 'T' is not assignable to type 'string'.
141+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures3.ts:54:10: This type parameter might need an `extends string` constraint.
138142
var b5: <T, U>(x: (arg: T) => U) => T;
139143
a5 = b5; // ok
140144
b5 = a5; // ok
@@ -227,6 +231,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
227231
!!! error TS2322: Type '{ a: T; b: T; }' is not assignable to type '{ a: string; b: number; }'.
228232
!!! error TS2322: Types of property 'a' are incompatible.
229233
!!! error TS2322: Type 'T' is not assignable to type 'string'.
234+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures3.ts:84:11: This type parameter might need an `extends string` constraint.
230235
var b15: <T>(x: T) => T[];
231236
a15 = b15; // ok
232237
b15 = a15; // ok

tests/baselines/reference/assignmentCompatWithCallSignatures4.errors.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
113113
!!! error TS2322: Type '(x: number) => string[]' is not assignable to type '<T, U>(x: T) => U[]'.
114114
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
115115
!!! error TS2322: Type 'T' is not assignable to type 'number'.
116+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures4.ts:43:18: This type parameter might need an `extends number` constraint.
116117

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

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

227230
// target type has generic call signature
228231
var a3: <T>(x: T) => string[];
@@ -232,6 +235,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
232235
!!! error TS2322: Type '<T>(x: T) => T[]' is not assignable to type '<T>(x: T) => string[]'.
233236
!!! error TS2322: Type 'T[]' is not assignable to type 'string[]'.
234237
!!! error TS2322: Type 'T' is not assignable to type 'string'.
238+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures4.ts:93:18: This type parameter might need an `extends string` constraint.
235239
b3 = a3;
236240
~~
237241
!!! error TS2322: Type '<T>(x: T) => string[]' is not assignable to type '<T>(x: T) => T[]'.

tests/baselines/reference/assignmentCompatWithCallSignatures5.errors.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
8484
!!! error TS2322: Types of property 'foo' are incompatible.
8585
!!! error TS2322: Type 'U' is not assignable to type 'T'.
8686
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'U'.
87+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures5.ts:50:14: This type parameter might need an `extends T` constraint.
8788
var b15: <U, V>(x: { a: U; b: V; }) => U[];
8889
a15 = b15; // ok, T = U, T = V
8990
b15 = a15; // ok
@@ -94,6 +95,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
9495
!!! error TS2322: Types of property 'b' are incompatible.
9596
!!! error TS2322: Type 'V' is not assignable to type 'U'.
9697
!!! error TS2322: 'U' could be instantiated with an arbitrary type which could be unrelated to 'V'.
98+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures5.ts:53:14: This type parameter might need an `extends U` constraint.
9799
var b16: <T>(x: { a: T; b: T }) => T[];
98100
a15 = b16; // ok
99101
b15 = a16; // ok
@@ -103,6 +105,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
103105
!!! error TS2322: Type '{ a: U; b: V; }' is not assignable to type '{ a: Base; b: Base; }'.
104106
!!! error TS2322: Types of property 'a' are incompatible.
105107
!!! error TS2322: Type 'U' is not assignable to type 'Base'.
108+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures5.ts:53:11: This type parameter might need an `extends Base` constraint.
106109
var b17: <T>(x: (a: T) => T) => T[];
107110
a17 = b17; // ok
108111
b17 = a17; // ok

tests/baselines/reference/assignmentCompatWithCallSignatures6.errors.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
6565
!!! error TS2322: Types of property 'foo' are incompatible.
6666
!!! error TS2322: Type 'U' is not assignable to type 'T'.
6767
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'U'.
68+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures6.ts:37:14: This type parameter might need an `extends T` constraint.
6869
var b16: <T>(x: { a: T; b: T }) => T[];
6970
x.a16 = b16;
7071
b16 = x.a16;
@@ -73,4 +74,5 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
7374
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
7475
!!! error TS2322: Type '{ a: T; b: T; }' is not assignable to type '{ a: Base; b: Base; }'.
7576
!!! error TS2322: Types of property 'a' are incompatible.
76-
!!! error TS2322: Type 'T' is not assignable to type 'Base'.
77+
!!! error TS2322: Type 'T' is not assignable to type 'Base'.
78+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithCallSignatures6.ts:40:11: This type parameter might need an `extends Base` constraint.

tests/baselines/reference/assignmentCompatWithConstructSignatures3.errors.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,27 +114,31 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
114114
!!! error TS2322: Type 'new (x: number) => number[]' is not assignable to type 'new <T>(x: T) => T[]'.
115115
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
116116
!!! error TS2322: Type 'T' is not assignable to type 'number'.
117+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithConstructSignatures3.ts:45:13: This type parameter might need an `extends number` constraint.
117118
var b2: new <T>(x: T) => string[];
118119
a2 = b2; // ok
119120
b2 = a2; // ok
120121
~~
121122
!!! error TS2322: Type 'new (x: number) => string[]' is not assignable to type 'new <T>(x: T) => string[]'.
122123
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
123124
!!! error TS2322: Type 'T' is not assignable to type 'number'.
125+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithConstructSignatures3.ts:48:14: This type parameter might need an `extends number` constraint.
124126
var b3: new <T>(x: T) => T;
125127
a3 = b3; // ok
126128
b3 = a3; // ok
127129
~~
128130
!!! error TS2322: Type 'new (x: number) => void' is not assignable to type 'new <T>(x: T) => T'.
129131
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
130132
!!! error TS2322: Type 'T' is not assignable to type 'number'.
133+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithConstructSignatures3.ts:51:14: This type parameter might need an `extends number` constraint.
131134
var b4: new <T, U>(x: T, y: U) => T;
132135
a4 = b4; // ok
133136
b4 = a4; // ok
134137
~~
135138
!!! error TS2322: Type 'new (x: string, y: number) => string' is not assignable to type 'new <T, U>(x: T, y: U) => T'.
136139
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
137140
!!! error TS2322: Type 'T' is not assignable to type 'string'.
141+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithConstructSignatures3.ts:54:14: This type parameter might need an `extends string` constraint.
138142
var b5: new <T, U>(x: (arg: T) => U) => T;
139143
a5 = b5; // ok
140144
b5 = a5; // ok
@@ -227,6 +231,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
227231
!!! error TS2322: Type '{ a: T; b: T; }' is not assignable to type '{ a: string; b: number; }'.
228232
!!! error TS2322: Types of property 'a' are incompatible.
229233
!!! error TS2322: Type 'T' is not assignable to type 'string'.
234+
!!! related TS2208 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithConstructSignatures3.ts:84:15: This type parameter might need an `extends string` constraint.
230235
var b15: new <T>(x: T) => T[];
231236
a15 = b15; // ok
232237
b15 = a15; // ok

0 commit comments

Comments
 (0)