Skip to content

Commit 51e7ae0

Browse files
Kingwlsandersn
authored andcommitted
provide spelling suggestion for indexed access (microsoft#22225)
* provide spelling suggestion for indexed access * update merge * accept baseline * fix suggession return type * allow string or identifier on getSuggestionForNonexistentProperty * fix lint
1 parent 662ca71 commit 51e7ae0

9 files changed

+86
-7
lines changed

src/compiler/checker.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -8845,7 +8845,15 @@ namespace ts {
88458845
error(accessExpression.argumentExpression, Diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number);
88468846
}
88478847
else {
8848-
error(accessExpression, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(objectType));
8848+
let suggestion: string | undefined;
8849+
if (propName !== undefined && (suggestion = getSuggestionForNonexistentProperty(propName as string, objectType))) {
8850+
if (suggestion !== undefined) {
8851+
error(accessExpression.argumentExpression, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, propName as string, typeToString(objectType), suggestion);
8852+
}
8853+
}
8854+
else {
8855+
error(accessExpression, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(objectType));
8856+
}
88498857
}
88508858
}
88518859
return anyType;
@@ -17367,14 +17375,21 @@ namespace ts {
1736717375
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion);
1736817376
}
1736917377
else {
17370-
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
17378+
const suggestion = getSuggestionForNonexistentProperty(propNode, containingType);
17379+
if (suggestion !== undefined) {
17380+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion);
17381+
}
17382+
else {
17383+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
17384+
}
1737117385
}
1737217386
}
17387+
1737317388
diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo));
1737417389
}
1737517390

17376-
function getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined {
17377-
const suggestion = getSpellingSuggestionForName(idText(node), getPropertiesOfType(containingType), SymbolFlags.Value);
17391+
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
17392+
const suggestion = getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
1737817393
return suggestion && symbolName(suggestion);
1737917394
}
1738017395

src/compiler/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2976,7 +2976,7 @@ namespace ts {
29762976
*/
29772977
/* @internal */ tryGetMemberInModuleExportsAndProperties(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
29782978
getApparentType(type: Type): Type;
2979-
getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined;
2979+
getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
29802980
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
29812981
getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined;
29822982
getBaseConstraintOfType(type: Type): Type | undefined;

tests/baselines/reference/api/tsserverlibrary.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2659,7 +2659,7 @@ declare namespace ts {
26592659
*/
26602660
tryGetMemberInModuleExportsAndProperties(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
26612661
getApparentType(type: Type): Type;
2662-
getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined;
2662+
getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
26632663
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
26642664
getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined;
26652665
getBaseConstraintOfType(type: Type): Type | undefined;

tests/baselines/reference/api/typescript.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1904,7 +1904,7 @@ declare namespace ts {
19041904
getAmbientModules(): Symbol[];
19051905
tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
19061906
getApparentType(type: Type): Type;
1907-
getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined;
1907+
getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
19081908
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
19091909
getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined;
19101910
getBaseConstraintOfType(type: Type): Type | undefined;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
tests/cases/compiler/indexedAccessImplicitlyAny.ts(3,3): error TS2551: Property 'foo' does not exist on type 'I'. Did you mean 'foof'?
2+
tests/cases/compiler/indexedAccessImplicitlyAny.ts(4,3): error TS2551: Property 'foo' does not exist on type 'I'. Did you mean 'foof'?
3+
4+
5+
==== tests/cases/compiler/indexedAccessImplicitlyAny.ts (2 errors) ====
6+
interface I { foof: number };
7+
declare const i: I;
8+
i.foo;
9+
~~~
10+
!!! error TS2551: Property 'foo' does not exist on type 'I'. Did you mean 'foof'?
11+
i["foo"];
12+
~~~~~
13+
!!! error TS2551: Property 'foo' does not exist on type 'I'. Did you mean 'foof'?
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [indexedAccessImplicitlyAny.ts]
2+
interface I { foof: number };
3+
declare const i: I;
4+
i.foo;
5+
i["foo"];
6+
7+
//// [indexedAccessImplicitlyAny.js]
8+
;
9+
i.foo;
10+
i["foo"];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
=== tests/cases/compiler/indexedAccessImplicitlyAny.ts ===
2+
interface I { foof: number };
3+
>I : Symbol(I, Decl(indexedAccessImplicitlyAny.ts, 0, 0))
4+
>foof : Symbol(I.foof, Decl(indexedAccessImplicitlyAny.ts, 0, 13))
5+
6+
declare const i: I;
7+
>i : Symbol(i, Decl(indexedAccessImplicitlyAny.ts, 1, 13))
8+
>I : Symbol(I, Decl(indexedAccessImplicitlyAny.ts, 0, 0))
9+
10+
i.foo;
11+
>i : Symbol(i, Decl(indexedAccessImplicitlyAny.ts, 1, 13))
12+
13+
i["foo"];
14+
>i : Symbol(i, Decl(indexedAccessImplicitlyAny.ts, 1, 13))
15+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/compiler/indexedAccessImplicitlyAny.ts ===
2+
interface I { foof: number };
3+
>I : I
4+
>foof : number
5+
6+
declare const i: I;
7+
>i : I
8+
>I : I
9+
10+
i.foo;
11+
>i.foo : any
12+
>i : I
13+
>foo : any
14+
15+
i["foo"];
16+
>i["foo"] : any
17+
>i : I
18+
>"foo" : "foo"
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @target: esnext
2+
// @noImplicitAny: true
3+
4+
interface I { foof: number };
5+
declare const i: I;
6+
i.foo;
7+
i["foo"];

0 commit comments

Comments
 (0)