provide spelling suggestion for indexed access (#22225)
* provide spelling suggestion for indexed access * update merge * accept baseline * fix suggession return type * allow string or identifier on getSuggestionForNonexistentProperty * fix lint
This commit is contained in:
parent
662ca71efc
commit
51e7ae0813
|
@ -8845,7 +8845,15 @@ namespace ts {
|
|||
error(accessExpression.argumentExpression, Diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number);
|
||||
}
|
||||
else {
|
||||
error(accessExpression, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(objectType));
|
||||
let suggestion: string | undefined;
|
||||
if (propName !== undefined && (suggestion = getSuggestionForNonexistentProperty(propName as string, objectType))) {
|
||||
if (suggestion !== undefined) {
|
||||
error(accessExpression.argumentExpression, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, propName as string, typeToString(objectType), suggestion);
|
||||
}
|
||||
}
|
||||
else {
|
||||
error(accessExpression, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(objectType));
|
||||
}
|
||||
}
|
||||
}
|
||||
return anyType;
|
||||
|
@ -17367,14 +17375,21 @@ namespace ts {
|
|||
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion);
|
||||
}
|
||||
else {
|
||||
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
|
||||
const suggestion = getSuggestionForNonexistentProperty(propNode, containingType);
|
||||
if (suggestion !== undefined) {
|
||||
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion);
|
||||
}
|
||||
else {
|
||||
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo));
|
||||
}
|
||||
|
||||
function getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined {
|
||||
const suggestion = getSpellingSuggestionForName(idText(node), getPropertiesOfType(containingType), SymbolFlags.Value);
|
||||
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
|
||||
const suggestion = getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
|
||||
return suggestion && symbolName(suggestion);
|
||||
}
|
||||
|
||||
|
|
|
@ -2976,7 +2976,7 @@ namespace ts {
|
|||
*/
|
||||
/* @internal */ tryGetMemberInModuleExportsAndProperties(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
|
||||
getApparentType(type: Type): Type;
|
||||
getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined;
|
||||
getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
|
||||
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
|
||||
getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined;
|
||||
getBaseConstraintOfType(type: Type): Type | undefined;
|
||||
|
|
|
@ -2659,7 +2659,7 @@ declare namespace ts {
|
|||
*/
|
||||
tryGetMemberInModuleExportsAndProperties(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
|
||||
getApparentType(type: Type): Type;
|
||||
getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined;
|
||||
getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
|
||||
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
|
||||
getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined;
|
||||
getBaseConstraintOfType(type: Type): Type | undefined;
|
||||
|
|
|
@ -1904,7 +1904,7 @@ declare namespace ts {
|
|||
getAmbientModules(): Symbol[];
|
||||
tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
|
||||
getApparentType(type: Type): Type;
|
||||
getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined;
|
||||
getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
|
||||
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
|
||||
getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined;
|
||||
getBaseConstraintOfType(type: Type): Type | undefined;
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
tests/cases/compiler/indexedAccessImplicitlyAny.ts(3,3): error TS2551: Property 'foo' does not exist on type 'I'. Did you mean 'foof'?
|
||||
tests/cases/compiler/indexedAccessImplicitlyAny.ts(4,3): error TS2551: Property 'foo' does not exist on type 'I'. Did you mean 'foof'?
|
||||
|
||||
|
||||
==== tests/cases/compiler/indexedAccessImplicitlyAny.ts (2 errors) ====
|
||||
interface I { foof: number };
|
||||
declare const i: I;
|
||||
i.foo;
|
||||
~~~
|
||||
!!! error TS2551: Property 'foo' does not exist on type 'I'. Did you mean 'foof'?
|
||||
i["foo"];
|
||||
~~~~~
|
||||
!!! error TS2551: Property 'foo' does not exist on type 'I'. Did you mean 'foof'?
|
10
tests/baselines/reference/indexedAccessImplicitlyAny.js
Normal file
10
tests/baselines/reference/indexedAccessImplicitlyAny.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
//// [indexedAccessImplicitlyAny.ts]
|
||||
interface I { foof: number };
|
||||
declare const i: I;
|
||||
i.foo;
|
||||
i["foo"];
|
||||
|
||||
//// [indexedAccessImplicitlyAny.js]
|
||||
;
|
||||
i.foo;
|
||||
i["foo"];
|
15
tests/baselines/reference/indexedAccessImplicitlyAny.symbols
Normal file
15
tests/baselines/reference/indexedAccessImplicitlyAny.symbols
Normal file
|
@ -0,0 +1,15 @@
|
|||
=== tests/cases/compiler/indexedAccessImplicitlyAny.ts ===
|
||||
interface I { foof: number };
|
||||
>I : Symbol(I, Decl(indexedAccessImplicitlyAny.ts, 0, 0))
|
||||
>foof : Symbol(I.foof, Decl(indexedAccessImplicitlyAny.ts, 0, 13))
|
||||
|
||||
declare const i: I;
|
||||
>i : Symbol(i, Decl(indexedAccessImplicitlyAny.ts, 1, 13))
|
||||
>I : Symbol(I, Decl(indexedAccessImplicitlyAny.ts, 0, 0))
|
||||
|
||||
i.foo;
|
||||
>i : Symbol(i, Decl(indexedAccessImplicitlyAny.ts, 1, 13))
|
||||
|
||||
i["foo"];
|
||||
>i : Symbol(i, Decl(indexedAccessImplicitlyAny.ts, 1, 13))
|
||||
|
19
tests/baselines/reference/indexedAccessImplicitlyAny.types
Normal file
19
tests/baselines/reference/indexedAccessImplicitlyAny.types
Normal file
|
@ -0,0 +1,19 @@
|
|||
=== tests/cases/compiler/indexedAccessImplicitlyAny.ts ===
|
||||
interface I { foof: number };
|
||||
>I : I
|
||||
>foof : number
|
||||
|
||||
declare const i: I;
|
||||
>i : I
|
||||
>I : I
|
||||
|
||||
i.foo;
|
||||
>i.foo : any
|
||||
>i : I
|
||||
>foo : any
|
||||
|
||||
i["foo"];
|
||||
>i["foo"] : any
|
||||
>i : I
|
||||
>"foo" : "foo"
|
||||
|
7
tests/cases/compiler/indexedAccessImplicitlyAny.ts
Normal file
7
tests/cases/compiler/indexedAccessImplicitlyAny.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
// @target: esnext
|
||||
// @noImplicitAny: true
|
||||
|
||||
interface I { foof: number };
|
||||
declare const i: I;
|
||||
i.foo;
|
||||
i["foo"];
|
Loading…
Reference in a new issue