Skip to content

Commit

Permalink
Improve error messages when indexing into a type (#31379)
Browse files Browse the repository at this point in the history
* Improved error messages when indexing an object type with a literal string, a literal string union or a string.

* Added more specific message when using the indexing operator with an incompatible index argument.

* Fixed spelling and error message.
  • Loading branch information
dragomirtitian authored and weswigham committed May 23, 2019
1 parent a2b4029 commit 8ab0a25
Show file tree
Hide file tree
Showing 16 changed files with 312 additions and 38 deletions.
32 changes: 27 additions & 5 deletions src/compiler/checker.ts
Expand Up @@ -10067,7 +10067,7 @@ namespace ts {
return false;
}

function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) {
function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) {
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
const propName = isTypeUsableAsPropertyName(indexType) ?
getPropertyNameFromType(indexType) :
Expand Down Expand Up @@ -10141,7 +10141,7 @@ namespace ts {
if (objectType.symbol === globalThisSymbol && propName !== undefined && globalThisSymbol.exports!.has(propName) && (globalThisSymbol.exports!.get(propName)!.flags & SymbolFlags.BlockScoped)) {
error(accessExpression, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(propName), typeToString(objectType));
}
else if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors) {
else if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors && !suppressNoImplicitAnyError) {
if (propName !== undefined && typeHasStaticProperty(propName, objectType)) {
error(accessExpression, Diagnostics.Property_0_is_a_static_member_of_type_1, propName as string, typeToString(objectType));
}
Expand All @@ -10161,7 +10161,29 @@ namespace ts {
error(accessExpression, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature_Did_you_mean_to_call_1, typeToString(objectType), suggestion);
}
else {
error(accessExpression, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(objectType));
let errorInfo: DiagnosticMessageChain | undefined;
if (indexType.flags & TypeFlags.EnumLiteral) {
errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, "[" + typeToString(indexType) + "]", typeToString(objectType));
}
else if (indexType.flags & TypeFlags.UniqueESSymbol) {
const symbolName = getFullyQualifiedName((indexType as UniqueESSymbolType).symbol, accessExpression);
errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, "[" + symbolName + "]", typeToString(objectType));
}
else if (indexType.flags & TypeFlags.StringLiteral) {
errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, (indexType as StringLiteralType).value, typeToString(objectType));
}
else if (indexType.flags & TypeFlags.NumberLiteral) {
errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, (indexType as NumberLiteralType).value, typeToString(objectType));
}
else if (indexType.flags & (TypeFlags.Number | TypeFlags.String)) {
errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.No_index_signature_with_a_parameter_of_type_0_was_found_on_type_1, typeToString(indexType), typeToString(objectType));
}

errorInfo = chainDiagnosticMessages(
errorInfo,
Diagnostics.Element_implicitly_has_an_any_type_because_expression_of_type_0_can_t_be_used_to_index_type_1, typeToString(fullIndexType), typeToString(objectType)
);
diagnostics.add(createDiagnosticForNodeFromMessageChain(accessExpression, errorInfo));
}
}
}
Expand Down Expand Up @@ -10360,7 +10382,7 @@ namespace ts {
const propTypes: Type[] = [];
let wasMissingProp = false;
for (const t of (<UnionType>indexType).types) {
const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, accessNode, accessFlags);
const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, indexType, wasMissingProp, accessNode, accessFlags);
if (propType) {
propTypes.push(propType);
}
Expand All @@ -10378,7 +10400,7 @@ namespace ts {
}
return accessFlags & AccessFlags.Writing ? getIntersectionType(propTypes) : getUnionType(propTypes);
}
return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, accessNode, accessFlags | AccessFlags.CacheSymbol);
return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, /* supressNoImplicitAnyError */ false, accessNode, accessFlags | AccessFlags.CacheSymbol);
}

function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Expand Up @@ -4288,6 +4288,14 @@
"category": "Error",
"code": 7052
},
"Element implicitly has an 'any' type because expression of type '{0}' can't be used to index type '{1}'.": {
"category": "Error",
"code": 7053
},
"No index signature with a parameter of type '{0}' was found on type '{1}'.": {
"category": "Error",
"code": 7054
},
"You cannot rename this element.": {
"category": "Error",
"code": 8000
Expand Down
Expand Up @@ -2,8 +2,10 @@ tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(4,5): error TS2
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(5,6): error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(6,12): error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(8,5): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(9,1): error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(10,1): error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(9,1): error TS7053: Element implicitly has an 'any' type because expression of type '"hi"' can't be used to index type 'typeof globalThis'.
Property 'hi' does not exist on type 'typeof globalThis'.
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(10,1): error TS7053: Element implicitly has an 'any' type because expression of type '"hi"' can't be used to index type 'typeof globalThis'.
Property 'hi' does not exist on type 'typeof globalThis'.


==== tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts (6 errors) ====
Expand All @@ -25,8 +27,10 @@ tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(10,1): error TS
!!! error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
this['hi']
~~~~~~~~~~
!!! error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
!!! error TS7053: Element implicitly has an 'any' type because expression of type '"hi"' can't be used to index type 'typeof globalThis'.
!!! error TS7053: Property 'hi' does not exist on type 'typeof globalThis'.
globalThis['hi']
~~~~~~~~~~~~~~~~
!!! error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
!!! error TS7053: Element implicitly has an 'any' type because expression of type '"hi"' can't be used to index type 'typeof globalThis'.
!!! error TS7053: Property 'hi' does not exist on type 'typeof globalThis'.

6 changes: 4 additions & 2 deletions tests/baselines/reference/keyofAndIndexedAccess2.errors.txt
Expand Up @@ -15,7 +15,8 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(26,7): error TS233
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(27,5): error TS2322: Type '1' is not assignable to type 'T[keyof T]'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(31,5): error TS2322: Type '{ [key: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(38,5): error TS2322: Type '{ [x: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(50,3): error TS7017: Element implicitly has an 'any' type because type 'Item' has no index signature.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(50,3): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Item'.
No index signature with a parameter of type 'string' was found on type 'Item'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(51,3): error TS2322: Type '123' is not assignable to type 'string & number'.
Type '123' is not assignable to type 'string'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(52,3): error TS2322: Type '123' is not assignable to type 'T[keyof T]'.
Expand Down Expand Up @@ -112,7 +113,8 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(108,5): error TS23
function f10<T extends Item, K extends keyof T>(obj: T, k1: string, k2: keyof Item, k3: keyof T, k4: K) {
obj[k1] = 123; // Error
~~~~~~~
!!! error TS7017: Element implicitly has an 'any' type because type 'Item' has no index signature.
!!! error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Item'.
!!! error TS7053: No index signature with a parameter of type 'string' was found on type 'Item'.
obj[k2] = 123; // Error
~~~~~~~
!!! error TS2322: Type '123' is not assignable to type 'string & number'.
Expand Down
12 changes: 8 additions & 4 deletions tests/baselines/reference/noImplicitAnyForIn.errors.txt
@@ -1,5 +1,7 @@
tests/cases/compiler/noImplicitAnyForIn.ts(7,18): error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
tests/cases/compiler/noImplicitAnyForIn.ts(14,18): error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
tests/cases/compiler/noImplicitAnyForIn.ts(7,18): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
No index signature with a parameter of type 'string' was found on type '{}'.
tests/cases/compiler/noImplicitAnyForIn.ts(14,18): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
No index signature with a parameter of type 'string' was found on type '{}'.
tests/cases/compiler/noImplicitAnyForIn.ts(28,5): error TS7005: Variable 'n' implicitly has an 'any[][]' type.
tests/cases/compiler/noImplicitAnyForIn.ts(30,6): error TS2405: The left-hand side of a 'for...in' statement must be of type 'string' or 'any'.

Expand All @@ -13,7 +15,8 @@ tests/cases/compiler/noImplicitAnyForIn.ts(30,6): error TS2405: The left-hand si
//Should yield an implicit 'any' error
var _j = x[i][j];
~~~~~~~
!!! error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
!!! error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
!!! error TS7053: No index signature with a parameter of type 'string' was found on type '{}'.
}

for (var k in x[0]) {
Expand All @@ -22,7 +25,8 @@ tests/cases/compiler/noImplicitAnyForIn.ts(30,6): error TS2405: The left-hand si
//Should yield an implicit 'any' error
var k2 = k1[k];
~~~~~
!!! error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
!!! error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
!!! error TS7053: No index signature with a parameter of type 'string' was found on type '{}'.
}
}

Expand Down
16 changes: 10 additions & 6 deletions tests/baselines/reference/noImplicitAnyIndexing.errors.txt
@@ -1,7 +1,9 @@
tests/cases/compiler/noImplicitAnyIndexing.ts(12,37): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
tests/cases/compiler/noImplicitAnyIndexing.ts(19,9): error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
tests/cases/compiler/noImplicitAnyIndexing.ts(22,9): error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
tests/cases/compiler/noImplicitAnyIndexing.ts(30,10): error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
tests/cases/compiler/noImplicitAnyIndexing.ts(19,9): error TS7053: Element implicitly has an 'any' type because expression of type '"hi"' can't be used to index type '{}'.
Property 'hi' does not exist on type '{}'.
tests/cases/compiler/noImplicitAnyIndexing.ts(22,9): error TS7053: Element implicitly has an 'any' type because expression of type '10' can't be used to index type '{}'.
Property '10' does not exist on type '{}'.
tests/cases/compiler/noImplicitAnyIndexing.ts(30,10): error TS7053: Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{}'.


==== tests/cases/compiler/noImplicitAnyIndexing.ts (4 errors) ====
Expand All @@ -27,12 +29,14 @@ tests/cases/compiler/noImplicitAnyIndexing.ts(30,10): error TS7017: Element impl
// Should report an implicit 'any'.
var x = {}["hi"];
~~~~~~~~
!!! error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
!!! error TS7053: Element implicitly has an 'any' type because expression of type '"hi"' can't be used to index type '{}'.
!!! error TS7053: Property 'hi' does not exist on type '{}'.

// Should report an implicit 'any'.
var y = {}[10];
~~~~~~
!!! error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
!!! error TS7053: Element implicitly has an 'any' type because expression of type '10' can't be used to index type '{}'.
!!! error TS7053: Property '10' does not exist on type '{}'.


var hi: any = "hi";
Expand All @@ -42,7 +46,7 @@ tests/cases/compiler/noImplicitAnyIndexing.ts(30,10): error TS7017: Element impl
// Should report an implicit 'any'.
var z1 = emptyObj[hi];
~~~~~~~~~~~~
!!! error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
!!! error TS7053: Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{}'.
var z2 = (<any>emptyObj)[hi];

interface MyMap<T> {
Expand Down

0 comments on commit 8ab0a25

Please sign in to comment.