Skip to content

Commit 6cd229b

Browse files
authored
Merge pull request #30769 from Microsoft/saferIndexedAccessTypes
Improve soundness of indexed access types
2 parents 4230270 + 6785472 commit 6cd229b

19 files changed

+1602
-264
lines changed

src/compiler/checker.ts

Lines changed: 178 additions & 126 deletions
Large diffs are not rendered by default.

src/compiler/parser.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7797,7 +7797,7 @@ namespace ts {
77977797
const referencedFiles = context.referencedFiles;
77987798
const typeReferenceDirectives = context.typeReferenceDirectives;
77997799
const libReferenceDirectives = context.libReferenceDirectives;
7800-
forEach(toArray(entryOrList), (arg: PragmaPseudoMap["reference"]) => {
7800+
forEach(toArray(entryOrList) as PragmaPseudoMap["reference"][], arg => {
78017801
const { types, lib, path } = arg.arguments;
78027802
if (arg.arguments["no-default-lib"]) {
78037803
context.hasNoDefaultLib = true;
@@ -7819,8 +7819,8 @@ namespace ts {
78197819
}
78207820
case "amd-dependency": {
78217821
context.amdDependencies = map(
7822-
toArray(entryOrList),
7823-
(x: PragmaPseudoMap["amd-dependency"]) => ({ name: x.arguments.name, path: x.arguments.path }));
7822+
toArray(entryOrList) as PragmaPseudoMap["amd-dependency"][],
7823+
x => ({ name: x.arguments.name, path: x.arguments.path }));
78247824
break;
78257825
}
78267826
case "amd-module": {

src/compiler/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4279,7 +4279,8 @@ namespace ts {
42794279
objectType: Type;
42804280
indexType: Type;
42814281
constraint?: Type;
4282-
simplified?: Type;
4282+
simplifiedForReading?: Type;
4283+
simplifiedForWriting?: Type;
42834284
}
42844285

42854286
export type TypeVariable = TypeParameter | IndexedAccessType;

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2359,7 +2359,8 @@ declare namespace ts {
23592359
objectType: Type;
23602360
indexType: Type;
23612361
constraint?: Type;
2362-
simplified?: Type;
2362+
simplifiedForReading?: Type;
2363+
simplifiedForWriting?: Type;
23632364
}
23642365
type TypeVariable = TypeParameter | IndexedAccessType;
23652366
interface IndexType extends InstantiableType {

tests/baselines/reference/api/typescript.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2359,7 +2359,8 @@ declare namespace ts {
23592359
objectType: Type;
23602360
indexType: Type;
23612361
constraint?: Type;
2362-
simplified?: Type;
2362+
simplifiedForReading?: Type;
2363+
simplifiedForWriting?: Type;
23632364
}
23642365
type TypeVariable = TypeParameter | IndexedAccessType;
23652366
interface IndexType extends InstantiableType {

tests/baselines/reference/circularlyConstrainedMappedTypeContainingConditionalNoInfiniteInstantiationDepth.errors.txt

Lines changed: 32 additions & 50 deletions
Large diffs are not rendered by default.

tests/baselines/reference/infiniteConstraints.errors.txt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
tests/cases/compiler/infiniteConstraints.ts(3,37): error TS2589: Type instantiation is excessively deep and possibly infinite.
21
tests/cases/compiler/infiniteConstraints.ts(4,37): error TS2536: Type '"val"' cannot be used to index type 'B[Exclude<keyof B, K>]'.
32
tests/cases/compiler/infiniteConstraints.ts(21,21): error TS2536: Type '"val"' cannot be used to index type 'Extract<T[K], Record<"val", string>>'.
43
tests/cases/compiler/infiniteConstraints.ts(21,57): error TS2536: Type '"val"' cannot be used to index type 'Extract<T[Exclude<keyof T, K>], Record<"val", string>>'.
@@ -7,12 +6,10 @@ tests/cases/compiler/infiniteConstraints.ts(31,63): error TS2322: Type 'Record<"
76
tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' cannot be used to index type 'T[keyof T]'.
87

98

10-
==== tests/cases/compiler/infiniteConstraints.ts (7 errors) ====
9+
==== tests/cases/compiler/infiniteConstraints.ts (6 errors) ====
1110
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
1211

1312
type T1<B extends { [K in keyof B]: Extract<B[Exclude<keyof B, K>], { val: string }>["val"] }> = B;
14-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15-
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
1613
type T2<B extends { [K in keyof B]: B[Exclude<keyof B, K>]["val"] }> = B;
1714
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1815
!!! error TS2536: Type '"val"' cannot be used to index type 'B[Exclude<keyof B, K>]'.

tests/baselines/reference/keyofAndIndexedAccess.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1794,7 +1794,7 @@ function updateIds2<T extends { [x: string]: string }, K extends keyof T>(
17941794
>key : K
17951795

17961796
stringMap[x]; // Should be OK.
1797-
>stringMap[x] : { [oldId: string]: string; }[T[K]]
1797+
>stringMap[x] : string
17981798
>stringMap : { [oldId: string]: string; }
17991799
>x : T[K]
18001800
}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(4,5): error TS2322: Type '"x"' is not assignable to type 'number'.
2+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(6,5): error TS2322: Type '2' is not assignable to type '0 | 1'.
3+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(7,5): error TS2322: Type '"x"' is not assignable to type '0 | 1'.
4+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(8,5): error TS2322: Type '1' is not assignable to type 'never'.
5+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(9,5): error TS2322: Type '2' is not assignable to type 'never'.
6+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(10,5): error TS2322: Type '"x"' is not assignable to type 'never'.
7+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(14,5): error TS2739: Type '{ [key: string]: number; }' is missing the following properties from type '{ x: number; y: number; }': x, y
8+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(15,5): error TS2322: Type 'T' is not assignable to type '{ x: number; y: number; }'.
9+
Type '{ [key: string]: number; }' is missing the following properties from type '{ x: number; y: number; }': x, y
10+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(18,5): error TS2322: Type '{ x: number; y: number; }' is not assignable to type 'T'.
11+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(19,5): error TS2322: Type '{ [key: string]: number; }' is not assignable to type 'T'.
12+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(26,7): error TS2339: Property 'x' does not exist on type 'T'.
13+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(27,5): error TS2322: Type '1' is not assignable to type 'T[keyof T]'.
14+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(31,5): error TS2322: Type '{ [key: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
15+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(38,5): error TS2322: Type '{ [x: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
16+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(50,3): error TS7017: Element implicitly has an 'any' type because type 'Item' has no index signature.
17+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(51,3): error TS2322: Type '123' is not assignable to type 'string & number'.
18+
Type '123' is not assignable to type 'string'.
19+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(52,3): error TS2322: Type '123' is not assignable to type 'T[keyof T]'.
20+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(53,3): error TS2322: Type '123' is not assignable to type 'T[K]'.
21+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(65,7): error TS2339: Property 'foo' does not exist on type 'T'.
22+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(66,3): error TS2536: Type 'string' cannot be used to index type 'T'.
23+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(67,3): error TS2322: Type '123' is not assignable to type 'T[keyof T]'.
24+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(68,3): error TS2322: Type '123' is not assignable to type 'T[K]'.
25+
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(108,5): error TS2322: Type '123' is not assignable to type 'Type[K]'.
26+
Type '123' is not assignable to type '123 & "some string"'.
27+
Type '123' is not assignable to type '"some string"'.
28+
29+
30+
==== tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts (23 errors) ====
31+
function f1(obj: { a: number, b: 0 | 1, c: string }, k0: 'a', k1: 'a' | 'b', k2: 'a' | 'b' | 'c') {
32+
obj[k0] = 1;
33+
obj[k0] = 2;
34+
obj[k0] = 'x'; // Error
35+
~~~~~~~
36+
!!! error TS2322: Type '"x"' is not assignable to type 'number'.
37+
obj[k1] = 1;
38+
obj[k1] = 2; // Error
39+
~~~~~~~
40+
!!! error TS2322: Type '2' is not assignable to type '0 | 1'.
41+
obj[k1] = 'x'; // Error
42+
~~~~~~~
43+
!!! error TS2322: Type '"x"' is not assignable to type '0 | 1'.
44+
obj[k2] = 1; // Error
45+
~~~~~~~
46+
!!! error TS2322: Type '1' is not assignable to type 'never'.
47+
obj[k2] = 2; // Error
48+
~~~~~~~
49+
!!! error TS2322: Type '2' is not assignable to type 'never'.
50+
obj[k2] = 'x'; // Error
51+
~~~~~~~
52+
!!! error TS2322: Type '"x"' is not assignable to type 'never'.
53+
}
54+
55+
function f2<T extends { [key: string]: number }>(a: { x: number, y: number }, b: { [key: string]: number }, c: T, k: keyof T) {
56+
a = b; // Error, index signature in source doesn't imply properties are present
57+
~
58+
!!! error TS2739: Type '{ [key: string]: number; }' is missing the following properties from type '{ x: number; y: number; }': x, y
59+
a = c; // Error, index signature in source doesn't imply properties are present
60+
~
61+
!!! error TS2322: Type 'T' is not assignable to type '{ x: number; y: number; }'.
62+
!!! error TS2322: Type '{ [key: string]: number; }' is missing the following properties from type '{ x: number; y: number; }': x, y
63+
b = a;
64+
b = c;
65+
c = a; // Error, constraint on target doesn't imply any properties or signatures
66+
~
67+
!!! error TS2322: Type '{ x: number; y: number; }' is not assignable to type 'T'.
68+
c = b; // Error, constraint on target doesn't imply any properties or signatures
69+
~
70+
!!! error TS2322: Type '{ [key: string]: number; }' is not assignable to type 'T'.
71+
a.x;
72+
b.x;
73+
c.x;
74+
c[k];
75+
a.x = 1;
76+
b.x = 1;
77+
c.x = 1; // Error, cannot write to index signature through constraint
78+
~
79+
!!! error TS2339: Property 'x' does not exist on type 'T'.
80+
c[k] = 1; // Error, cannot write to index signature through constraint
81+
~~~~
82+
!!! error TS2322: Type '1' is not assignable to type 'T[keyof T]'.
83+
}
84+
85+
function f3<K extends string>(a: { [P in K]: number }, b: { [key: string]: number }, k: K) {
86+
a = b; // Error, index signature doesn't imply properties are present
87+
~
88+
!!! error TS2322: Type '{ [key: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
89+
b = a;
90+
a[k];
91+
a[k] = 1;
92+
}
93+
94+
function f3b<K extends string>(a: { [P in K]: number }, b: { [P in string]: number }, k: K) {
95+
a = b; // Error, index signature doesn't imply properties are present
96+
~
97+
!!! error TS2322: Type '{ [x: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
98+
b = a;
99+
}
100+
101+
function f4<K extends string>(a: { [key: string]: number }[K], b: number) {
102+
a = b;
103+
b = a;
104+
}
105+
106+
type Item = { a: string, b: number };
107+
108+
function f10<T extends Item, K extends keyof T>(obj: T, k1: string, k2: keyof Item, k3: keyof T, k4: K) {
109+
obj[k1] = 123; // Error
110+
~~~~~~~
111+
!!! error TS7017: Element implicitly has an 'any' type because type 'Item' has no index signature.
112+
obj[k2] = 123; // Error
113+
~~~~~~~
114+
!!! error TS2322: Type '123' is not assignable to type 'string & number'.
115+
!!! error TS2322: Type '123' is not assignable to type 'string'.
116+
obj[k3] = 123; // Error
117+
~~~~~~~
118+
!!! error TS2322: Type '123' is not assignable to type 'T[keyof T]'.
119+
obj[k4] = 123; // Error
120+
~~~~~~~
121+
!!! error TS2322: Type '123' is not assignable to type 'T[K]'.
122+
}
123+
124+
type Dict = Record<string, number>;
125+
126+
function f11<K extends keyof Dict>(obj: Dict, k1: keyof Dict, k2: K) {
127+
obj.foo = 123;
128+
obj[k1] = 123;
129+
obj[k2] = 123;
130+
}
131+
132+
function f12<T extends Readonly<Dict>, K extends keyof T>(obj: T, k1: keyof Dict, k2: keyof T, k3: K) {
133+
obj.foo = 123; // Error
134+
~~~
135+
!!! error TS2339: Property 'foo' does not exist on type 'T'.
136+
obj[k1] = 123; // Error
137+
~~~~~~~
138+
!!! error TS2536: Type 'string' cannot be used to index type 'T'.
139+
obj[k2] = 123; // Error
140+
~~~~~~~
141+
!!! error TS2322: Type '123' is not assignable to type 'T[keyof T]'.
142+
obj[k3] = 123; // Error
143+
~~~~~~~
144+
!!! error TS2322: Type '123' is not assignable to type 'T[K]'.
145+
}
146+
147+
// Repro from #27895
148+
149+
export interface Entity {
150+
id: number | string;
151+
}
152+
153+
export type IdOf<E extends Entity> = E['id'];
154+
155+
export interface EntityState<E extends Entity> {
156+
ids: IdOf<E>[];
157+
entities: { [key: string]: E, [key: number]: E };
158+
}
159+
160+
161+
export function getAllEntities<E extends Entity>(state: EntityState<E>): E[] {
162+
const { ids, entities } = state;
163+
return ids.map(id => entities[id]);
164+
}
165+
166+
export function getEntity<E extends Entity>(id: IdOf<E>, state: EntityState<E>): E | undefined {
167+
const { ids, entities } = state;
168+
169+
if (!ids.includes(id)) {
170+
return undefined;
171+
}
172+
173+
return entities[id];
174+
}
175+
176+
// Repro from #30603
177+
178+
interface Type {
179+
a: 123;
180+
b: "some string";
181+
}
182+
183+
function get123<K extends keyof Type>(): Type[K] {
184+
return 123; // Error
185+
~~~~~~~~~~~
186+
!!! error TS2322: Type '123' is not assignable to type 'Type[K]'.
187+
!!! error TS2322: Type '123' is not assignable to type '123 & "some string"'.
188+
!!! error TS2322: Type '123' is not assignable to type '"some string"'.
189+
}
190+

0 commit comments

Comments
 (0)