feat(45679): support 'did you mean' diagnostics for string literal union (#45723)

* feat(45679): support 'did you mean' diagnostics for string literal union

* Format suggested type with `typeToString`

* Address feedback
This commit is contained in:
Hiroshi Ogawa 2021-09-15 00:53:36 +09:00 committed by GitHub
parent 8523ac8bc9
commit 4f8aa5239e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 107 additions and 4 deletions

View File

@ -17831,6 +17831,13 @@ namespace ts {
message = Diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_types_of_the_target_s_properties;
}
else {
if (source.flags & TypeFlags.StringLiteral && target.flags & TypeFlags.Union) {
const suggestedType = getSuggestedTypeForNonexistentStringLiteralType(source as StringLiteralType, target as UnionType);
if (suggestedType) {
reportError(Diagnostics.Type_0_is_not_assignable_to_type_1_Did_you_mean_2, generalizedSourceType, targetType, typeToString(suggestedType));
return;
}
}
message = Diagnostics.Type_0_is_not_assignable_to_type_1;
}
}
@ -28230,6 +28237,11 @@ namespace ts {
return suggestion;
}
function getSuggestedTypeForNonexistentStringLiteralType(source: StringLiteralType, target: UnionType): StringLiteralType | undefined {
const candidates = target.types.filter((type): type is StringLiteralType => !!(type.flags & TypeFlags.StringLiteral));
return getSpellingSuggestion(source.value, candidates, type => type.value);
}
/**
* Given a name and a list of symbols whose names are *not* equal to the name, return a spelling suggestion if there is one that is close enough.
* Names less than length 3 only check for case-insensitive equality, not levenshtein distance.

View File

@ -3300,6 +3300,10 @@
"category": "Error",
"code": 2819
},
"Type '{0}' is not assignable to type '{1}'. Did you mean '{2}'?": {
"category": "Error",
"code": 2820
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",

View File

@ -0,0 +1,20 @@
tests/cases/compiler/didYouMeanStringLiteral.ts(5,7): error TS2820: Type '"strong"' is not assignable to type 'T1'. Did you mean '"string"'?
tests/cases/compiler/didYouMeanStringLiteral.ts(6,7): error TS2322: Type '"strong"' is not assignable to type '"number" | "boolean"'.
tests/cases/compiler/didYouMeanStringLiteral.ts(7,7): error TS2820: Type '"strong"' is not assignable to type '"string" | "boolean"'. Did you mean '"string"'?
==== tests/cases/compiler/didYouMeanStringLiteral.ts (3 errors) ====
type T1 = "string" | "number" | "boolean";
type T2 = T1 & ("number" | "boolean"); // "number" | "boolean"
type T3 = T1 & ("string" | "boolean"); // "string" | "boolean"
const t1: T1 = "strong";
~~
!!! error TS2820: Type '"strong"' is not assignable to type 'T1'. Did you mean '"string"'?
const t2: T2 = "strong";
~~
!!! error TS2322: Type '"strong"' is not assignable to type '"number" | "boolean"'.
const t3: T3 = "strong";
~~
!!! error TS2820: Type '"strong"' is not assignable to type '"string" | "boolean"'. Did you mean '"string"'?

View File

@ -0,0 +1,14 @@
//// [didYouMeanStringLiteral.ts]
type T1 = "string" | "number" | "boolean";
type T2 = T1 & ("number" | "boolean"); // "number" | "boolean"
type T3 = T1 & ("string" | "boolean"); // "string" | "boolean"
const t1: T1 = "strong";
const t2: T2 = "strong";
const t3: T3 = "strong";
//// [didYouMeanStringLiteral.js]
var t1 = "strong";
var t2 = "strong";
var t3 = "strong";

View File

@ -0,0 +1,24 @@
=== tests/cases/compiler/didYouMeanStringLiteral.ts ===
type T1 = "string" | "number" | "boolean";
>T1 : Symbol(T1, Decl(didYouMeanStringLiteral.ts, 0, 0))
type T2 = T1 & ("number" | "boolean"); // "number" | "boolean"
>T2 : Symbol(T2, Decl(didYouMeanStringLiteral.ts, 0, 42))
>T1 : Symbol(T1, Decl(didYouMeanStringLiteral.ts, 0, 0))
type T3 = T1 & ("string" | "boolean"); // "string" | "boolean"
>T3 : Symbol(T3, Decl(didYouMeanStringLiteral.ts, 1, 38))
>T1 : Symbol(T1, Decl(didYouMeanStringLiteral.ts, 0, 0))
const t1: T1 = "strong";
>t1 : Symbol(t1, Decl(didYouMeanStringLiteral.ts, 4, 5))
>T1 : Symbol(T1, Decl(didYouMeanStringLiteral.ts, 0, 0))
const t2: T2 = "strong";
>t2 : Symbol(t2, Decl(didYouMeanStringLiteral.ts, 5, 5))
>T2 : Symbol(T2, Decl(didYouMeanStringLiteral.ts, 0, 42))
const t3: T3 = "strong";
>t3 : Symbol(t3, Decl(didYouMeanStringLiteral.ts, 6, 5))
>T3 : Symbol(T3, Decl(didYouMeanStringLiteral.ts, 1, 38))

View File

@ -0,0 +1,22 @@
=== tests/cases/compiler/didYouMeanStringLiteral.ts ===
type T1 = "string" | "number" | "boolean";
>T1 : T1
type T2 = T1 & ("number" | "boolean"); // "number" | "boolean"
>T2 : "number" | "boolean"
type T3 = T1 & ("string" | "boolean"); // "string" | "boolean"
>T3 : "string" | "boolean"
const t1: T1 = "strong";
>t1 : T1
>"strong" : "strong"
const t2: T2 = "strong";
>t2 : "number" | "boolean"
>"strong" : "strong"
const t3: T3 = "strong";
>t3 : "string" | "boolean"
>"strong" : "strong"

View File

@ -1,5 +1,5 @@
tests/cases/compiler/errorsForCallAndAssignmentAreSimilar.ts(11,11): error TS2322: Type '"hdpvd"' is not assignable to type '"hddvd" | "bluray"'.
tests/cases/compiler/errorsForCallAndAssignmentAreSimilar.ts(16,11): error TS2322: Type '"hdpvd"' is not assignable to type '"hddvd" | "bluray"'.
tests/cases/compiler/errorsForCallAndAssignmentAreSimilar.ts(11,11): error TS2820: Type '"hdpvd"' is not assignable to type '"hddvd" | "bluray"'. Did you mean '"hddvd"'?
tests/cases/compiler/errorsForCallAndAssignmentAreSimilar.ts(16,11): error TS2820: Type '"hdpvd"' is not assignable to type '"hddvd" | "bluray"'. Did you mean '"hddvd"'?
==== tests/cases/compiler/errorsForCallAndAssignmentAreSimilar.ts (2 errors) ====
@ -15,7 +15,7 @@ tests/cases/compiler/errorsForCallAndAssignmentAreSimilar.ts(16,11): error TS232
{ kind: "bluray", },
{ kind: "hdpvd", }
~~~~
!!! error TS2322: Type '"hdpvd"' is not assignable to type '"hddvd" | "bluray"'.
!!! error TS2820: Type '"hdpvd"' is not assignable to type '"hddvd" | "bluray"'. Did you mean '"hddvd"'?
!!! related TS6500 tests/cases/compiler/errorsForCallAndAssignmentAreSimilar.ts:3:13: The expected type comes from property 'kind' which is declared here on type 'Disc'
]);
@ -23,7 +23,7 @@ tests/cases/compiler/errorsForCallAndAssignmentAreSimilar.ts(16,11): error TS232
{ kind: "bluray", },
{ kind: "hdpvd", }
~~~~
!!! error TS2322: Type '"hdpvd"' is not assignable to type '"hddvd" | "bluray"'.
!!! error TS2820: Type '"hdpvd"' is not assignable to type '"hddvd" | "bluray"'. Did you mean '"hddvd"'?
!!! related TS6500 tests/cases/compiler/errorsForCallAndAssignmentAreSimilar.ts:3:13: The expected type comes from property 'kind' which is declared here on type 'Disc'
];
}

View File

@ -0,0 +1,7 @@
type T1 = "string" | "number" | "boolean";
type T2 = T1 & ("number" | "boolean"); // "number" | "boolean"
type T3 = T1 & ("string" | "boolean"); // "string" | "boolean"
const t1: T1 = "strong";
const t2: T2 = "strong";
const t3: T3 = "strong";