Skip to content

Commit b1f5ef6

Browse files
authored
Improve error message for index signature on generic type when writing (#55906)
1 parent 4131798 commit b1f5ef6

8 files changed

+117
-5
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17845,7 +17845,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1784517845
if (indexInfo) {
1784617846
if (accessFlags & AccessFlags.NoIndexSignatures && indexInfo.keyType !== numberType) {
1784717847
if (accessExpression) {
17848-
error(accessExpression, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(originalObjectType));
17848+
if (accessFlags & AccessFlags.Writing) {
17849+
error(accessExpression, Diagnostics.Type_0_is_generic_and_can_only_be_indexed_for_reading, typeToString(originalObjectType));
17850+
}
17851+
else {
17852+
error(accessExpression, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(originalObjectType));
17853+
}
1784917854
}
1785017855
return undefined;
1785117856
}

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3699,6 +3699,10 @@
36993699
"category": "Error",
37003700
"code": 2861
37013701
},
3702+
"Type '{0}' is generic and can only be indexed for reading.": {
3703+
"category": "Error",
3704+
"code": 2862
3705+
},
37023706

37033707
"Import declaration '{0}' is using private name '{1}'.": {
37043708
"category": "Error",
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
cannotIndexGenericWritingError.ts(4,5): error TS2862: Type 'T' is generic and can only be indexed for reading.
2+
cannotIndexGenericWritingError.ts(8,5): error TS2862: Type 'T' is generic and can only be indexed for reading.
3+
4+
5+
==== cannotIndexGenericWritingError.ts (2 errors) ====
6+
// From #47357
7+
8+
function foo<T extends Record<string | symbol, any>>(target: T, p: string | symbol) {
9+
target[p] = ""; // error
10+
~~~~~~~~~
11+
!!! error TS2862: Type 'T' is generic and can only be indexed for reading.
12+
}
13+
14+
function foo2<T extends number[] & { [s: string]: number | string }>(target: T, p: string | number) {
15+
target[p] = 1; // error
16+
~~~~~~~~~
17+
!!! error TS2862: Type 'T' is generic and can only be indexed for reading.
18+
target[1] = 1; // ok
19+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [tests/cases/compiler/cannotIndexGenericWritingError.ts] ////
2+
3+
=== cannotIndexGenericWritingError.ts ===
4+
// From #47357
5+
6+
function foo<T extends Record<string | symbol, any>>(target: T, p: string | symbol) {
7+
>foo : Symbol(foo, Decl(cannotIndexGenericWritingError.ts, 0, 0))
8+
>T : Symbol(T, Decl(cannotIndexGenericWritingError.ts, 2, 13))
9+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
10+
>target : Symbol(target, Decl(cannotIndexGenericWritingError.ts, 2, 53))
11+
>T : Symbol(T, Decl(cannotIndexGenericWritingError.ts, 2, 13))
12+
>p : Symbol(p, Decl(cannotIndexGenericWritingError.ts, 2, 63))
13+
14+
target[p] = ""; // error
15+
>target : Symbol(target, Decl(cannotIndexGenericWritingError.ts, 2, 53))
16+
>p : Symbol(p, Decl(cannotIndexGenericWritingError.ts, 2, 63))
17+
}
18+
19+
function foo2<T extends number[] & { [s: string]: number | string }>(target: T, p: string | number) {
20+
>foo2 : Symbol(foo2, Decl(cannotIndexGenericWritingError.ts, 4, 1))
21+
>T : Symbol(T, Decl(cannotIndexGenericWritingError.ts, 6, 14))
22+
>s : Symbol(s, Decl(cannotIndexGenericWritingError.ts, 6, 38))
23+
>target : Symbol(target, Decl(cannotIndexGenericWritingError.ts, 6, 69))
24+
>T : Symbol(T, Decl(cannotIndexGenericWritingError.ts, 6, 14))
25+
>p : Symbol(p, Decl(cannotIndexGenericWritingError.ts, 6, 79))
26+
27+
target[p] = 1; // error
28+
>target : Symbol(target, Decl(cannotIndexGenericWritingError.ts, 6, 69))
29+
>p : Symbol(p, Decl(cannotIndexGenericWritingError.ts, 6, 79))
30+
31+
target[1] = 1; // ok
32+
>target : Symbol(target, Decl(cannotIndexGenericWritingError.ts, 6, 69))
33+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//// [tests/cases/compiler/cannotIndexGenericWritingError.ts] ////
2+
3+
=== cannotIndexGenericWritingError.ts ===
4+
// From #47357
5+
6+
function foo<T extends Record<string | symbol, any>>(target: T, p: string | symbol) {
7+
>foo : <T extends Record<string | symbol, any>>(target: T, p: string | symbol) => void
8+
>target : T
9+
>p : string | symbol
10+
11+
target[p] = ""; // error
12+
>target[p] = "" : ""
13+
>target[p] : any
14+
>target : T
15+
>p : string | symbol
16+
>"" : ""
17+
}
18+
19+
function foo2<T extends number[] & { [s: string]: number | string }>(target: T, p: string | number) {
20+
>foo2 : <T extends number[] & { [s: string]: string | number; }>(target: T, p: string | number) => void
21+
>s : string
22+
>target : T
23+
>p : string | number
24+
25+
target[p] = 1; // error
26+
>target[p] = 1 : 1
27+
>target[p] : any
28+
>target : T
29+
>p : string | number
30+
>1 : 1
31+
32+
target[1] = 1; // ok
33+
>target[1] = 1 : 1
34+
>target[1] : number
35+
>target : T
36+
>1 : 1
37+
>1 : 1
38+
}

tests/baselines/reference/keyofAndIndexedAccess2.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ keyofAndIndexedAccess2.ts(52,3): error TS2322: Type 'number' is not assignable t
2424
keyofAndIndexedAccess2.ts(53,3): error TS2322: Type 'number' is not assignable to type 'T[K]'.
2525
'T[K]' could be instantiated with an arbitrary type which could be unrelated to 'number'.
2626
keyofAndIndexedAccess2.ts(65,7): error TS2339: Property 'foo' does not exist on type 'T'.
27-
keyofAndIndexedAccess2.ts(66,3): error TS2536: Type 'string' cannot be used to index type 'T'.
27+
keyofAndIndexedAccess2.ts(66,3): error TS2862: Type 'T' is generic and can only be indexed for reading.
2828
keyofAndIndexedAccess2.ts(67,3): error TS2322: Type 'number' is not assignable to type 'T[keyof T]'.
2929
'number' is assignable to the constraint of type 'T[keyof T]', but 'T[keyof T]' could be instantiated with a different subtype of constraint 'number'.
3030
keyofAndIndexedAccess2.ts(68,3): error TS2322: Type 'number' is not assignable to type 'T[K]'.
@@ -146,7 +146,7 @@ keyofAndIndexedAccess2.ts(108,5): error TS2322: Type '123' is not assignable to
146146
!!! error TS2339: Property 'foo' does not exist on type 'T'.
147147
obj[k1] = 123; // Error
148148
~~~~~~~
149-
!!! error TS2536: Type 'string' cannot be used to index type 'T'.
149+
!!! error TS2862: Type 'T' is generic and can only be indexed for reading.
150150
obj[k2] = 123; // Error
151151
~~~~~~~
152152
!!! error TS2322: Type 'number' is not assignable to type 'T[keyof T]'.

tests/baselines/reference/mappedTypeGenericWithKnownKeys.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mappedTypeGenericWithKnownKeys.ts(9,9): error TS2551: Property 'unknownLiteralKey' does not exist on type 'Record<keyof Shape | "knownLiteralKey", number>'. Did you mean 'knownLiteralKey'?
2-
mappedTypeGenericWithKnownKeys.ts(10,5): error TS2536: Type 'string' cannot be used to index type 'Record<keyof Shape | "knownLiteralKey", number>'.
2+
mappedTypeGenericWithKnownKeys.ts(10,5): error TS2862: Type 'Record<keyof Shape | "knownLiteralKey", number>' is generic and can only be indexed for reading.
33

44

55
==== mappedTypeGenericWithKnownKeys.ts (2 errors) ====
@@ -16,6 +16,6 @@ mappedTypeGenericWithKnownKeys.ts(10,5): error TS2536: Type 'string' cannot be u
1616
!!! error TS2551: Property 'unknownLiteralKey' does not exist on type 'Record<keyof Shape | "knownLiteralKey", number>'. Did you mean 'knownLiteralKey'?
1717
obj['' as string] = 4; // error
1818
~~~~~~~~~~~~~~~~~
19-
!!! error TS2536: Type 'string' cannot be used to index type 'Record<keyof Shape | "knownLiteralKey", number>'.
19+
!!! error TS2862: Type 'Record<keyof Shape | "knownLiteralKey", number>' is generic and can only be indexed for reading.
2020
}
2121

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// From #47357
5+
6+
function foo<T extends Record<string | symbol, any>>(target: T, p: string | symbol) {
7+
target[p] = ""; // error
8+
}
9+
10+
function foo2<T extends number[] & { [s: string]: number | string }>(target: T, p: string | number) {
11+
target[p] = 1; // error
12+
target[1] = 1; // ok
13+
}

0 commit comments

Comments
 (0)