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:
Wenlu Wang 2018-06-22 04:40:41 +08:00 committed by Nathan Shively-Sanders
parent 662ca71efc
commit 51e7ae0813
9 changed files with 86 additions and 7 deletions

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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'?

View 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"];

View 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))

View 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"

View file

@ -0,0 +1,7 @@
// @target: esnext
// @noImplicitAny: true
interface I { foof: number };
declare const i: I;
i.foo;
i["foo"];