Skip to content

Commit f996bab

Browse files
authored
Preserve readonly on mapped index signatures (#55541)
1 parent 811a637 commit f996bab

File tree

6 files changed

+435
-6
lines changed

6 files changed

+435
-6
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13614,7 +13614,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1361413614
propNameType.flags & (TypeFlags.Number | TypeFlags.Enum) ? numberType :
1361513615
propNameType;
1361613616
const propType = instantiateType(templateType, appendTypeMapping(type.mapper, typeParameter, keyType));
13617-
const indexInfo = createIndexInfo(indexKeyType, propType, !!(templateModifiers & MappedTypeModifiers.IncludeReadonly));
13617+
const modifiersIndexInfo = getApplicableIndexInfo(modifiersType, propNameType);
13618+
const isReadonly = !!(templateModifiers & MappedTypeModifiers.IncludeReadonly ||
13619+
!(templateModifiers & MappedTypeModifiers.ExcludeReadonly) && modifiersIndexInfo?.isReadonly);
13620+
const indexInfo = createIndexInfo(indexKeyType, propType, isReadonly);
1361813621
indexInfos = appendIndexInfo(indexInfos, indexInfo, /*union*/ true);
1361913622
}
1362013623
}
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
//// [tests/cases/conformance/types/mapped/mappedTypeIndexSignatureModifiers.ts] ////
2+
3+
=== mappedTypeIndexSignatureModifiers.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/14295
5+
6+
interface Obj1 {
7+
>Obj1 : Symbol(Obj1, Decl(mappedTypeIndexSignatureModifiers.ts, 0, 0))
8+
9+
readonly [key: string]: string;
10+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 3, 14))
11+
}
12+
type Res1 = Pick<Obj1, keyof Obj1>
13+
>Res1 : Symbol(Res1, Decl(mappedTypeIndexSignatureModifiers.ts, 4, 1))
14+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
15+
>Obj1 : Symbol(Obj1, Decl(mappedTypeIndexSignatureModifiers.ts, 0, 0))
16+
>Obj1 : Symbol(Obj1, Decl(mappedTypeIndexSignatureModifiers.ts, 0, 0))
17+
18+
interface Obj2 {
19+
>Obj2 : Symbol(Obj2, Decl(mappedTypeIndexSignatureModifiers.ts, 5, 34))
20+
21+
concreteProp: 'hello';
22+
>concreteProp : Symbol(Obj2.concreteProp, Decl(mappedTypeIndexSignatureModifiers.ts, 7, 16))
23+
24+
readonly [key: string]: string;
25+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 9, 14))
26+
}
27+
type Res2 = Pick<Obj2, keyof Obj2>
28+
>Res2 : Symbol(Res2, Decl(mappedTypeIndexSignatureModifiers.ts, 10, 1))
29+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
30+
>Obj2 : Symbol(Obj2, Decl(mappedTypeIndexSignatureModifiers.ts, 5, 34))
31+
>Obj2 : Symbol(Obj2, Decl(mappedTypeIndexSignatureModifiers.ts, 5, 34))
32+
33+
interface Obj3 {
34+
>Obj3 : Symbol(Obj3, Decl(mappedTypeIndexSignatureModifiers.ts, 11, 34))
35+
36+
readonly [key: string]: string;
37+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 14, 14))
38+
39+
readonly [key: number]: 'foo';
40+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 15, 14))
41+
}
42+
type Res3 = Pick<Obj3, keyof Obj3>
43+
>Res3 : Symbol(Res3, Decl(mappedTypeIndexSignatureModifiers.ts, 16, 1))
44+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
45+
>Obj3 : Symbol(Obj3, Decl(mappedTypeIndexSignatureModifiers.ts, 11, 34))
46+
>Obj3 : Symbol(Obj3, Decl(mappedTypeIndexSignatureModifiers.ts, 11, 34))
47+
48+
interface Obj4 {
49+
>Obj4 : Symbol(Obj4, Decl(mappedTypeIndexSignatureModifiers.ts, 17, 34))
50+
51+
[key: string]: string;
52+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 20, 5))
53+
54+
readonly [key: number]: 'foo';
55+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 21, 14))
56+
}
57+
type Res4 = Pick<Obj4, keyof Obj4>
58+
>Res4 : Symbol(Res4, Decl(mappedTypeIndexSignatureModifiers.ts, 22, 1))
59+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
60+
>Obj4 : Symbol(Obj4, Decl(mappedTypeIndexSignatureModifiers.ts, 17, 34))
61+
>Obj4 : Symbol(Obj4, Decl(mappedTypeIndexSignatureModifiers.ts, 17, 34))
62+
63+
interface Obj5 {
64+
>Obj5 : Symbol(Obj5, Decl(mappedTypeIndexSignatureModifiers.ts, 23, 34))
65+
66+
readonly [key: string]: string;
67+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 26, 14))
68+
69+
[key: number]: 'foo';
70+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 27, 5))
71+
}
72+
type Res5 = Pick<Obj5, keyof Obj5>
73+
>Res5 : Symbol(Res5, Decl(mappedTypeIndexSignatureModifiers.ts, 28, 1))
74+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
75+
>Obj5 : Symbol(Obj5, Decl(mappedTypeIndexSignatureModifiers.ts, 23, 34))
76+
>Obj5 : Symbol(Obj5, Decl(mappedTypeIndexSignatureModifiers.ts, 23, 34))
77+
78+
type Identity<T> = { [P in keyof T]: T[P]; }
79+
>Identity : Symbol(Identity, Decl(mappedTypeIndexSignatureModifiers.ts, 29, 34))
80+
>T : Symbol(T, Decl(mappedTypeIndexSignatureModifiers.ts, 31, 14))
81+
>P : Symbol(P, Decl(mappedTypeIndexSignatureModifiers.ts, 31, 22))
82+
>T : Symbol(T, Decl(mappedTypeIndexSignatureModifiers.ts, 31, 14))
83+
>T : Symbol(T, Decl(mappedTypeIndexSignatureModifiers.ts, 31, 14))
84+
>P : Symbol(P, Decl(mappedTypeIndexSignatureModifiers.ts, 31, 22))
85+
86+
interface Obj6 {
87+
>Obj6 : Symbol(Obj6, Decl(mappedTypeIndexSignatureModifiers.ts, 31, 44))
88+
89+
readonly [key: `wow${string}`]: 'foo';
90+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 34, 14))
91+
}
92+
type Res6 = Identity<Obj6>
93+
>Res6 : Symbol(Res6, Decl(mappedTypeIndexSignatureModifiers.ts, 35, 1))
94+
>Identity : Symbol(Identity, Decl(mappedTypeIndexSignatureModifiers.ts, 29, 34))
95+
>Obj6 : Symbol(Obj6, Decl(mappedTypeIndexSignatureModifiers.ts, 31, 44))
96+
97+
interface Obj7 {
98+
>Obj7 : Symbol(Obj7, Decl(mappedTypeIndexSignatureModifiers.ts, 36, 26))
99+
100+
[key: string]: string;
101+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 39, 5))
102+
103+
readonly [key: `wow${string}`]: 'foo';
104+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 40, 14))
105+
}
106+
type Res7 = Identity<Obj7>
107+
>Res7 : Symbol(Res7, Decl(mappedTypeIndexSignatureModifiers.ts, 41, 1))
108+
>Identity : Symbol(Identity, Decl(mappedTypeIndexSignatureModifiers.ts, 29, 34))
109+
>Obj7 : Symbol(Obj7, Decl(mappedTypeIndexSignatureModifiers.ts, 36, 26))
110+
111+
interface Obj8 {
112+
>Obj8 : Symbol(Obj8, Decl(mappedTypeIndexSignatureModifiers.ts, 42, 26))
113+
114+
readonly [key: string]: string;
115+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 45, 14))
116+
117+
[key: `wow${string}`]: 'foo';
118+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 46, 5))
119+
}
120+
type Res8 = Identity<Obj8>
121+
>Res8 : Symbol(Res8, Decl(mappedTypeIndexSignatureModifiers.ts, 47, 1))
122+
>Identity : Symbol(Identity, Decl(mappedTypeIndexSignatureModifiers.ts, 29, 34))
123+
>Obj8 : Symbol(Obj8, Decl(mappedTypeIndexSignatureModifiers.ts, 42, 26))
124+
125+
type StrippingPick<T, K extends keyof T> = { -readonly [P in K]: T[P]; }
126+
>StrippingPick : Symbol(StrippingPick, Decl(mappedTypeIndexSignatureModifiers.ts, 48, 26))
127+
>T : Symbol(T, Decl(mappedTypeIndexSignatureModifiers.ts, 50, 19))
128+
>K : Symbol(K, Decl(mappedTypeIndexSignatureModifiers.ts, 50, 21))
129+
>T : Symbol(T, Decl(mappedTypeIndexSignatureModifiers.ts, 50, 19))
130+
>P : Symbol(P, Decl(mappedTypeIndexSignatureModifiers.ts, 50, 56))
131+
>K : Symbol(K, Decl(mappedTypeIndexSignatureModifiers.ts, 50, 21))
132+
>T : Symbol(T, Decl(mappedTypeIndexSignatureModifiers.ts, 50, 19))
133+
>P : Symbol(P, Decl(mappedTypeIndexSignatureModifiers.ts, 50, 56))
134+
135+
interface Obj9 {
136+
>Obj9 : Symbol(Obj9, Decl(mappedTypeIndexSignatureModifiers.ts, 50, 72))
137+
138+
readonly [key: string]: string;
139+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 53, 14))
140+
}
141+
type Res9 = StrippingPick<Obj9, keyof Obj9>
142+
>Res9 : Symbol(Res9, Decl(mappedTypeIndexSignatureModifiers.ts, 54, 1))
143+
>StrippingPick : Symbol(StrippingPick, Decl(mappedTypeIndexSignatureModifiers.ts, 48, 26))
144+
>Obj9 : Symbol(Obj9, Decl(mappedTypeIndexSignatureModifiers.ts, 50, 72))
145+
>Obj9 : Symbol(Obj9, Decl(mappedTypeIndexSignatureModifiers.ts, 50, 72))
146+
147+
interface Obj10 {
148+
>Obj10 : Symbol(Obj10, Decl(mappedTypeIndexSignatureModifiers.ts, 55, 43))
149+
150+
readonly [key: string]: string;
151+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 58, 14))
152+
153+
readonly [key: number]: 'foo';
154+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 59, 14))
155+
}
156+
type Res10 = StrippingPick<Obj10, keyof Obj10>
157+
>Res10 : Symbol(Res10, Decl(mappedTypeIndexSignatureModifiers.ts, 60, 1))
158+
>StrippingPick : Symbol(StrippingPick, Decl(mappedTypeIndexSignatureModifiers.ts, 48, 26))
159+
>Obj10 : Symbol(Obj10, Decl(mappedTypeIndexSignatureModifiers.ts, 55, 43))
160+
>Obj10 : Symbol(Obj10, Decl(mappedTypeIndexSignatureModifiers.ts, 55, 43))
161+
162+
interface Obj11 {
163+
>Obj11 : Symbol(Obj11, Decl(mappedTypeIndexSignatureModifiers.ts, 61, 46))
164+
165+
[key: string]: string;
166+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 64, 5))
167+
168+
readonly [key: number]: 'foo';
169+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 65, 14))
170+
}
171+
type Res11 = StrippingPick<Obj11, keyof Obj11>
172+
>Res11 : Symbol(Res11, Decl(mappedTypeIndexSignatureModifiers.ts, 66, 1))
173+
>StrippingPick : Symbol(StrippingPick, Decl(mappedTypeIndexSignatureModifiers.ts, 48, 26))
174+
>Obj11 : Symbol(Obj11, Decl(mappedTypeIndexSignatureModifiers.ts, 61, 46))
175+
>Obj11 : Symbol(Obj11, Decl(mappedTypeIndexSignatureModifiers.ts, 61, 46))
176+
177+
interface Obj12 {
178+
>Obj12 : Symbol(Obj12, Decl(mappedTypeIndexSignatureModifiers.ts, 67, 46))
179+
180+
readonly [key: string]: string;
181+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 70, 14))
182+
183+
[key: number]: 'foo';
184+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 71, 5))
185+
}
186+
type Res12 = StrippingPick<Obj12, keyof Obj12>
187+
>Res12 : Symbol(Res12, Decl(mappedTypeIndexSignatureModifiers.ts, 72, 1))
188+
>StrippingPick : Symbol(StrippingPick, Decl(mappedTypeIndexSignatureModifiers.ts, 48, 26))
189+
>Obj12 : Symbol(Obj12, Decl(mappedTypeIndexSignatureModifiers.ts, 67, 46))
190+
>Obj12 : Symbol(Obj12, Decl(mappedTypeIndexSignatureModifiers.ts, 67, 46))
191+
192+
type StrippingIdentity<T> = { -readonly [P in keyof T]: T[P]; }
193+
>StrippingIdentity : Symbol(StrippingIdentity, Decl(mappedTypeIndexSignatureModifiers.ts, 73, 46))
194+
>T : Symbol(T, Decl(mappedTypeIndexSignatureModifiers.ts, 75, 23))
195+
>P : Symbol(P, Decl(mappedTypeIndexSignatureModifiers.ts, 75, 41))
196+
>T : Symbol(T, Decl(mappedTypeIndexSignatureModifiers.ts, 75, 23))
197+
>T : Symbol(T, Decl(mappedTypeIndexSignatureModifiers.ts, 75, 23))
198+
>P : Symbol(P, Decl(mappedTypeIndexSignatureModifiers.ts, 75, 41))
199+
200+
interface Obj13 {
201+
>Obj13 : Symbol(Obj13, Decl(mappedTypeIndexSignatureModifiers.ts, 75, 63))
202+
203+
readonly [key: `wow${string}`]: 'foo';
204+
>key : Symbol(key, Decl(mappedTypeIndexSignatureModifiers.ts, 78, 14))
205+
}
206+
type Res13 = StrippingIdentity<Obj13>
207+
>Res13 : Symbol(Res13, Decl(mappedTypeIndexSignatureModifiers.ts, 79, 1))
208+
>StrippingIdentity : Symbol(StrippingIdentity, Decl(mappedTypeIndexSignatureModifiers.ts, 73, 46))
209+
>Obj13 : Symbol(Obj13, Decl(mappedTypeIndexSignatureModifiers.ts, 75, 63))
210+
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
//// [tests/cases/conformance/types/mapped/mappedTypeIndexSignatureModifiers.ts] ////
2+
3+
=== mappedTypeIndexSignatureModifiers.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/14295
5+
6+
interface Obj1 {
7+
readonly [key: string]: string;
8+
>key : string
9+
}
10+
type Res1 = Pick<Obj1, keyof Obj1>
11+
>Res1 : { readonly [x: string]: string; readonly [x: number]: string; }
12+
13+
interface Obj2 {
14+
concreteProp: 'hello';
15+
>concreteProp : "hello"
16+
17+
readonly [key: string]: string;
18+
>key : string
19+
}
20+
type Res2 = Pick<Obj2, keyof Obj2>
21+
>Res2 : { readonly [x: string]: string; readonly [x: number]: string; }
22+
23+
interface Obj3 {
24+
readonly [key: string]: string;
25+
>key : string
26+
27+
readonly [key: number]: 'foo';
28+
>key : number
29+
}
30+
type Res3 = Pick<Obj3, keyof Obj3>
31+
>Res3 : { readonly [x: string]: string; readonly [x: number]: "foo"; }
32+
33+
interface Obj4 {
34+
[key: string]: string;
35+
>key : string
36+
37+
readonly [key: number]: 'foo';
38+
>key : number
39+
}
40+
type Res4 = Pick<Obj4, keyof Obj4>
41+
>Res4 : { [x: string]: string; readonly [x: number]: "foo"; }
42+
43+
interface Obj5 {
44+
readonly [key: string]: string;
45+
>key : string
46+
47+
[key: number]: 'foo';
48+
>key : number
49+
}
50+
type Res5 = Pick<Obj5, keyof Obj5>
51+
>Res5 : { readonly [x: string]: string; [x: number]: "foo"; }
52+
53+
type Identity<T> = { [P in keyof T]: T[P]; }
54+
>Identity : Identity<T>
55+
56+
interface Obj6 {
57+
readonly [key: `wow${string}`]: 'foo';
58+
>key : `wow${string}`
59+
}
60+
type Res6 = Identity<Obj6>
61+
>Res6 : Identity<Obj6>
62+
63+
interface Obj7 {
64+
[key: string]: string;
65+
>key : string
66+
67+
readonly [key: `wow${string}`]: 'foo';
68+
>key : `wow${string}`
69+
}
70+
type Res7 = Identity<Obj7>
71+
>Res7 : Identity<Obj7>
72+
73+
interface Obj8 {
74+
readonly [key: string]: string;
75+
>key : string
76+
77+
[key: `wow${string}`]: 'foo';
78+
>key : `wow${string}`
79+
}
80+
type Res8 = Identity<Obj8>
81+
>Res8 : Identity<Obj8>
82+
83+
type StrippingPick<T, K extends keyof T> = { -readonly [P in K]: T[P]; }
84+
>StrippingPick : StrippingPick<T, K>
85+
86+
interface Obj9 {
87+
readonly [key: string]: string;
88+
>key : string
89+
}
90+
type Res9 = StrippingPick<Obj9, keyof Obj9>
91+
>Res9 : { [x: string]: string; [x: number]: string; }
92+
93+
interface Obj10 {
94+
readonly [key: string]: string;
95+
>key : string
96+
97+
readonly [key: number]: 'foo';
98+
>key : number
99+
}
100+
type Res10 = StrippingPick<Obj10, keyof Obj10>
101+
>Res10 : { [x: string]: string; [x: number]: "foo"; }
102+
103+
interface Obj11 {
104+
[key: string]: string;
105+
>key : string
106+
107+
readonly [key: number]: 'foo';
108+
>key : number
109+
}
110+
type Res11 = StrippingPick<Obj11, keyof Obj11>
111+
>Res11 : { [x: string]: string; [x: number]: "foo"; }
112+
113+
interface Obj12 {
114+
readonly [key: string]: string;
115+
>key : string
116+
117+
[key: number]: 'foo';
118+
>key : number
119+
}
120+
type Res12 = StrippingPick<Obj12, keyof Obj12>
121+
>Res12 : { [x: string]: string; [x: number]: "foo"; }
122+
123+
type StrippingIdentity<T> = { -readonly [P in keyof T]: T[P]; }
124+
>StrippingIdentity : StrippingIdentity<T>
125+
126+
interface Obj13 {
127+
readonly [key: `wow${string}`]: 'foo';
128+
>key : `wow${string}`
129+
}
130+
type Res13 = StrippingIdentity<Obj13>
131+
>Res13 : StrippingIdentity<Obj13>
132+

tests/baselines/reference/mappedTypes1.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type T30 = { [P in keyof any]: void };
4141
>T30 : { [x: string]: void; }
4242

4343
type T31 = { [P in keyof string]: void };
44-
>T31 : { [x: number]: void; toString: void; charAt: void; charCodeAt: void; concat: void; indexOf: void; lastIndexOf: void; localeCompare: void; match: void; replace: void; search: void; slice: void; split: void; substring: void; toLowerCase: void; toLocaleLowerCase: void; toUpperCase: void; toLocaleUpperCase: void; trim: void; readonly length: void; substr: void; valueOf: void; }
44+
>T31 : { readonly [x: number]: void; toString: void; charAt: void; charCodeAt: void; concat: void; indexOf: void; lastIndexOf: void; localeCompare: void; match: void; replace: void; search: void; slice: void; split: void; substring: void; toLowerCase: void; toLocaleLowerCase: void; toUpperCase: void; toLocaleUpperCase: void; trim: void; readonly length: void; substr: void; valueOf: void; }
4545

4646
type T32 = { [P in keyof number]: void };
4747
>T32 : { toString: void; toFixed: void; toExponential: void; toPrecision: void; valueOf: void; toLocaleString: void; }

tests/baselines/reference/staticIndexSignature5.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,16 @@ type TE = keyof typeof B;
4040
>B : typeof B
4141

4242
type TF = Pick<typeof B, number>
43-
>TF : { [x: number]: 42 | 233; }
43+
>TF : { readonly [x: number]: 42 | 233; }
4444
>B : typeof B
4545

4646
type TFI = Pick<I, number>
47-
>TFI : { [x: number]: 42 | 233; }
47+
>TFI : { readonly [x: number]: 42 | 233; }
4848

4949
type TG = Omit<typeof B, number>
50-
>TG : { [x: string]: number; }
50+
>TG : { readonly [x: string]: number; }
5151
>B : typeof B
5252

5353
type TGI = Omit<I, number>
54-
>TGI : { [x: string]: number; }
54+
>TGI : { readonly [x: string]: number; }
5555

0 commit comments

Comments
 (0)