Skip to content

Commit 8ab0a25

Browse files
dragomirtitianweswigham
authored andcommitted
Improve error messages when indexing into a type (#31379)
* 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.
1 parent a2b4029 commit 8ab0a25

16 files changed

+312
-38
lines changed

src/compiler/checker.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10067,7 +10067,7 @@ namespace ts {
1006710067
return false;
1006810068
}
1006910069

10070-
function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) {
10070+
function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) {
1007110071
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
1007210072
const propName = isTypeUsableAsPropertyName(indexType) ?
1007310073
getPropertyNameFromType(indexType) :
@@ -10141,7 +10141,7 @@ namespace ts {
1014110141
if (objectType.symbol === globalThisSymbol && propName !== undefined && globalThisSymbol.exports!.has(propName) && (globalThisSymbol.exports!.get(propName)!.flags & SymbolFlags.BlockScoped)) {
1014210142
error(accessExpression, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(propName), typeToString(objectType));
1014310143
}
10144-
else if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors) {
10144+
else if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors && !suppressNoImplicitAnyError) {
1014510145
if (propName !== undefined && typeHasStaticProperty(propName, objectType)) {
1014610146
error(accessExpression, Diagnostics.Property_0_is_a_static_member_of_type_1, propName as string, typeToString(objectType));
1014710147
}
@@ -10161,7 +10161,29 @@ namespace ts {
1016110161
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);
1016210162
}
1016310163
else {
10164-
error(accessExpression, Diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, typeToString(objectType));
10164+
let errorInfo: DiagnosticMessageChain | undefined;
10165+
if (indexType.flags & TypeFlags.EnumLiteral) {
10166+
errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, "[" + typeToString(indexType) + "]", typeToString(objectType));
10167+
}
10168+
else if (indexType.flags & TypeFlags.UniqueESSymbol) {
10169+
const symbolName = getFullyQualifiedName((indexType as UniqueESSymbolType).symbol, accessExpression);
10170+
errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, "[" + symbolName + "]", typeToString(objectType));
10171+
}
10172+
else if (indexType.flags & TypeFlags.StringLiteral) {
10173+
errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, (indexType as StringLiteralType).value, typeToString(objectType));
10174+
}
10175+
else if (indexType.flags & TypeFlags.NumberLiteral) {
10176+
errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, (indexType as NumberLiteralType).value, typeToString(objectType));
10177+
}
10178+
else if (indexType.flags & (TypeFlags.Number | TypeFlags.String)) {
10179+
errorInfo = chainDiagnosticMessages(/* details */ undefined, Diagnostics.No_index_signature_with_a_parameter_of_type_0_was_found_on_type_1, typeToString(indexType), typeToString(objectType));
10180+
}
10181+
10182+
errorInfo = chainDiagnosticMessages(
10183+
errorInfo,
10184+
Diagnostics.Element_implicitly_has_an_any_type_because_expression_of_type_0_can_t_be_used_to_index_type_1, typeToString(fullIndexType), typeToString(objectType)
10185+
);
10186+
diagnostics.add(createDiagnosticForNodeFromMessageChain(accessExpression, errorInfo));
1016510187
}
1016610188
}
1016710189
}
@@ -10360,7 +10382,7 @@ namespace ts {
1036010382
const propTypes: Type[] = [];
1036110383
let wasMissingProp = false;
1036210384
for (const t of (<UnionType>indexType).types) {
10363-
const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, accessNode, accessFlags);
10385+
const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, indexType, wasMissingProp, accessNode, accessFlags);
1036410386
if (propType) {
1036510387
propTypes.push(propType);
1036610388
}
@@ -10378,7 +10400,7 @@ namespace ts {
1037810400
}
1037910401
return accessFlags & AccessFlags.Writing ? getIntersectionType(propTypes) : getUnionType(propTypes);
1038010402
}
10381-
return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, accessNode, accessFlags | AccessFlags.CacheSymbol);
10403+
return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, /* supressNoImplicitAnyError */ false, accessNode, accessFlags | AccessFlags.CacheSymbol);
1038210404
}
1038310405

1038410406
function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4288,6 +4288,14 @@
42884288
"category": "Error",
42894289
"code": 7052
42904290
},
4291+
"Element implicitly has an 'any' type because expression of type '{0}' can't be used to index type '{1}'.": {
4292+
"category": "Error",
4293+
"code": 7053
4294+
},
4295+
"No index signature with a parameter of type '{0}' was found on type '{1}'.": {
4296+
"category": "Error",
4297+
"code": 7054
4298+
},
42914299
"You cannot rename this element.": {
42924300
"category": "Error",
42934301
"code": 8000

tests/baselines/reference/globalThisUnknownNoImplicitAny.errors.txt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(4,5): error TS2
22
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(5,6): error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
33
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(6,12): error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
44
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(8,5): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
5-
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(9,1): error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
6-
tests/cases/conformance/es2019/globalThisUnknownNoImplicitAny.ts(10,1): error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
5+
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'.
6+
Property 'hi' does not exist on type 'typeof globalThis'.
7+
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'.
8+
Property 'hi' does not exist on type 'typeof globalThis'.
79

810

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

tests/baselines/reference/keyofAndIndexedAccess2.errors.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(26,7): error TS233
1515
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(27,5): error TS2322: Type '1' is not assignable to type 'T[keyof T]'.
1616
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(31,5): error TS2322: Type '{ [key: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
1717
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(38,5): error TS2322: Type '{ [x: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
18-
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(50,3): error TS7017: Element implicitly has an 'any' type because type 'Item' has no index signature.
18+
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'.
19+
No index signature with a parameter of type 'string' was found on type 'Item'.
1920
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(51,3): error TS2322: Type '123' is not assignable to type 'string & number'.
2021
Type '123' is not assignable to type 'string'.
2122
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(52,3): error TS2322: Type '123' is not assignable to type 'T[keyof T]'.
@@ -112,7 +113,8 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(108,5): error TS23
112113
function f10<T extends Item, K extends keyof T>(obj: T, k1: string, k2: keyof Item, k3: keyof T, k4: K) {
113114
obj[k1] = 123; // Error
114115
~~~~~~~
115-
!!! error TS7017: Element implicitly has an 'any' type because type 'Item' has no index signature.
116+
!!! error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Item'.
117+
!!! error TS7053: No index signature with a parameter of type 'string' was found on type 'Item'.
116118
obj[k2] = 123; // Error
117119
~~~~~~~
118120
!!! error TS2322: Type '123' is not assignable to type 'string & number'.

tests/baselines/reference/noImplicitAnyForIn.errors.txt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
tests/cases/compiler/noImplicitAnyForIn.ts(7,18): error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
2-
tests/cases/compiler/noImplicitAnyForIn.ts(14,18): error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
1+
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 '{}'.
2+
No index signature with a parameter of type 'string' was found on type '{}'.
3+
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 '{}'.
4+
No index signature with a parameter of type 'string' was found on type '{}'.
35
tests/cases/compiler/noImplicitAnyForIn.ts(28,5): error TS7005: Variable 'n' implicitly has an 'any[][]' type.
46
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'.
57

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

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

tests/baselines/reference/noImplicitAnyIndexing.errors.txt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
tests/cases/compiler/noImplicitAnyIndexing.ts(12,37): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
2-
tests/cases/compiler/noImplicitAnyIndexing.ts(19,9): error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
3-
tests/cases/compiler/noImplicitAnyIndexing.ts(22,9): error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
4-
tests/cases/compiler/noImplicitAnyIndexing.ts(30,10): error TS7017: Element implicitly has an 'any' type because type '{}' has no index signature.
2+
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 '{}'.
3+
Property 'hi' does not exist on type '{}'.
4+
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 '{}'.
5+
Property '10' does not exist on type '{}'.
6+
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 '{}'.
57

68

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

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

3741

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

4852
interface MyMap<T> {

0 commit comments

Comments
 (0)