Skip to content

Commit a626e4e

Browse files
committed
don't drop index signatures when spreading object
Fixes: microsoft#27273
1 parent b8def16 commit a626e4e

8 files changed

+248
-50
lines changed

src/compiler/checker.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6949,7 +6949,7 @@ namespace ts {
69496949
}
69506950

69516951
function unionSpreadIndexInfos(info1: IndexInfo | undefined, info2: IndexInfo | undefined): IndexInfo | undefined {
6952-
return info1 && info2 && createIndexInfo(
6952+
return !info1 ? info2 : !info2 ? info1 : createIndexInfo(
69536953
getUnionType([info1.type, info2.type]), info1.isReadonly || info2.isReadonly);
69546954
}
69556955

@@ -10273,6 +10273,24 @@ namespace ts {
1027310273
}
1027410274
}
1027510275

10276+
if (stringIndexInfo || numberIndexInfo) {
10277+
const addToNumberIndex: Type[] = [];
10278+
const addToStringIndex: Type[] = [];
10279+
members.forEach((symbol, key) => {
10280+
const type = getTypeOfSymbol(symbol);
10281+
addToStringIndex.push(type);
10282+
if (isNumericLiteralName(key)) {
10283+
addToNumberIndex.push(type);
10284+
}
10285+
});
10286+
if (stringIndexInfo) {
10287+
stringIndexInfo = createIndexInfo(getUnionType([stringIndexInfo.type, ...addToStringIndex]), stringIndexInfo.isReadonly);
10288+
}
10289+
if (numberIndexInfo) {
10290+
numberIndexInfo = createIndexInfo(getUnionType([numberIndexInfo.type, ...addToNumberIndex]), numberIndexInfo.isReadonly);
10291+
}
10292+
}
10293+
1027610294
const spread = createAnonymousType(
1027710295
symbol,
1027810296
members,

tests/baselines/reference/objectRest.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ var { [computed]: stillNotGreat, [computed2]: soSo, ...o } = o;
206206
({ [computed]: stillNotGreat, [computed2]: soSo, ...o } = o);
207207
>({ [computed]: stillNotGreat, [computed2]: soSo, ...o } = o) : { a: number; b: string; }
208208
>{ [computed]: stillNotGreat, [computed2]: soSo, ...o } = o : { a: number; b: string; }
209-
>{ [computed]: stillNotGreat, [computed2]: soSo, ...o } : { a: number; b: string; }
209+
>{ [computed]: stillNotGreat, [computed2]: soSo, ...o } : { [x: string]: any; a: number; b: string; }
210210
>[computed] : any
211211
>computed : string
212212
>stillNotGreat : any

tests/baselines/reference/objectSpreadComputedProperty.types

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,24 @@ function f() {
1616
>null : null
1717

1818
const o1 = { ...{}, [n]: n };
19-
>o1 : {}
20-
>{ ...{}, [n]: n } : {}
19+
>o1 : { [x: number]: number; }
20+
>{ ...{}, [n]: n } : { [x: number]: number; }
2121
>{} : {}
2222
>[n] : number
2323
>n : number
2424
>n : number
2525

2626
const o2 = { ...{}, [a]: n };
27-
>o2 : {}
28-
>{ ...{}, [a]: n } : {}
27+
>o2 : { [x: number]: number; }
28+
>{ ...{}, [a]: n } : { [x: number]: number; }
2929
>{} : {}
3030
>[a] : number
3131
>a : any
3232
>n : number
3333

3434
const o3 = { [a]: n, ...{}, [n]: n, ...{}, [m]: m };
35-
>o3 : {}
36-
>{ [a]: n, ...{}, [n]: n, ...{}, [m]: m } : {}
35+
>o3 : { [x: number]: number; }
36+
>{ [a]: n, ...{}, [n]: n, ...{}, [m]: m } : { [x: number]: number; }
3737
>[a] : number
3838
>a : any
3939
>n : number

tests/baselines/reference/objectSpreadIndexSignature.errors.txt

Lines changed: 0 additions & 23 deletions
This file was deleted.

tests/baselines/reference/objectSpreadIndexSignature.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
declare let indexed1: { [n: string]: number; a: number; };
33
declare let indexed2: { [n: string]: boolean; c: boolean; };
44
declare let indexed3: { [n: string]: number };
5+
declare let indexed4: { [n: number]: number };
56
let i = { ...indexed1, b: 11 };
67
// only indexed has indexer, so i[101]: any
78
i[101];
@@ -15,7 +16,24 @@ indexed3 = { ...b ? indexed3 : undefined };
1516
declare var roindex: { readonly [x:string]: number };
1617
var writable = { ...roindex };
1718
writable.a = 0; // should be ok.
18-
19+
20+
var mixed = { foo: true, ...indexed3, bar: 'bar' };
21+
mixed.foo; // boolean
22+
mixed.bar; // string
23+
mixed.baz; // string | number | boolean
24+
25+
var mixed2 = { foo: true, ...indexed4, bar: 'bar' };
26+
mixed2.foo; // boolean
27+
mixed2.bar; // string
28+
mixed2[1]; // number
29+
30+
var mixed3 = { ...indexed4, '1': 'foo'};
31+
mixed3[0]; // string | number
32+
mixed3[1]; // string
33+
34+
var mixed4 = { ...indexed3, '1': 'foo'};
35+
mixed4.foo; // string | number
36+
mixed4[1]; // string
1937

2038
//// [objectSpreadIndexSignature.js]
2139
"use strict";
@@ -39,3 +57,17 @@ ii[1001];
3957
indexed3 = __assign({}, b ? indexed3 : undefined);
4058
var writable = __assign({}, roindex);
4159
writable.a = 0; // should be ok.
60+
var mixed = __assign({ foo: true }, indexed3, { bar: 'bar' });
61+
mixed.foo; // boolean
62+
mixed.bar; // string
63+
mixed.baz; // string | number | boolean
64+
var mixed2 = __assign({ foo: true }, indexed4, { bar: 'bar' });
65+
mixed2.foo; // boolean
66+
mixed2.bar; // string
67+
mixed2[1]; // number
68+
var mixed3 = __assign({}, indexed4, { '1': 'foo' });
69+
mixed3[0]; // string | number
70+
mixed3[1]; // string
71+
var mixed4 = __assign({}, indexed3, { '1': 'foo' });
72+
mixed4.foo; // string | number
73+
mixed4[1]; // string

tests/baselines/reference/objectSpreadIndexSignature.symbols

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,41 +13,107 @@ declare let indexed3: { [n: string]: number };
1313
>indexed3 : Symbol(indexed3, Decl(objectSpreadIndexSignature.ts, 2, 11))
1414
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 2, 25))
1515

16+
declare let indexed4: { [n: number]: number };
17+
>indexed4 : Symbol(indexed4, Decl(objectSpreadIndexSignature.ts, 3, 11))
18+
>n : Symbol(n, Decl(objectSpreadIndexSignature.ts, 3, 25))
19+
1620
let i = { ...indexed1, b: 11 };
17-
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 3, 3))
21+
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 4, 3))
1822
>indexed1 : Symbol(indexed1, Decl(objectSpreadIndexSignature.ts, 0, 11))
19-
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 3, 22))
23+
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 4, 22))
2024

2125
// only indexed has indexer, so i[101]: any
2226
i[101];
23-
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 3, 3))
27+
>i : Symbol(i, Decl(objectSpreadIndexSignature.ts, 4, 3))
2428

2529
let ii = { ...indexed1, ...indexed2 };
26-
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 6, 3))
30+
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 7, 3))
2731
>indexed1 : Symbol(indexed1, Decl(objectSpreadIndexSignature.ts, 0, 11))
2832
>indexed2 : Symbol(indexed2, Decl(objectSpreadIndexSignature.ts, 1, 11))
2933

3034
// both have indexer, so i[1001]: number | boolean
3135
ii[1001];
32-
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 6, 3))
36+
>ii : Symbol(ii, Decl(objectSpreadIndexSignature.ts, 7, 3))
3337

3438
declare const b: boolean;
35-
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 10, 13))
39+
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 11, 13))
3640

3741
indexed3 = { ...b ? indexed3 : undefined };
3842
>indexed3 : Symbol(indexed3, Decl(objectSpreadIndexSignature.ts, 2, 11))
39-
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 10, 13))
43+
>b : Symbol(b, Decl(objectSpreadIndexSignature.ts, 11, 13))
4044
>indexed3 : Symbol(indexed3, Decl(objectSpreadIndexSignature.ts, 2, 11))
4145
>undefined : Symbol(undefined)
4246

4347
declare var roindex: { readonly [x:string]: number };
44-
>roindex : Symbol(roindex, Decl(objectSpreadIndexSignature.ts, 13, 11))
45-
>x : Symbol(x, Decl(objectSpreadIndexSignature.ts, 13, 33))
48+
>roindex : Symbol(roindex, Decl(objectSpreadIndexSignature.ts, 14, 11))
49+
>x : Symbol(x, Decl(objectSpreadIndexSignature.ts, 14, 33))
4650

4751
var writable = { ...roindex };
48-
>writable : Symbol(writable, Decl(objectSpreadIndexSignature.ts, 14, 3))
49-
>roindex : Symbol(roindex, Decl(objectSpreadIndexSignature.ts, 13, 11))
52+
>writable : Symbol(writable, Decl(objectSpreadIndexSignature.ts, 15, 3))
53+
>roindex : Symbol(roindex, Decl(objectSpreadIndexSignature.ts, 14, 11))
5054

5155
writable.a = 0; // should be ok.
52-
>writable : Symbol(writable, Decl(objectSpreadIndexSignature.ts, 14, 3))
56+
>writable : Symbol(writable, Decl(objectSpreadIndexSignature.ts, 15, 3))
57+
58+
var mixed = { foo: true, ...indexed3, bar: 'bar' };
59+
>mixed : Symbol(mixed, Decl(objectSpreadIndexSignature.ts, 18, 3))
60+
>foo : Symbol(foo, Decl(objectSpreadIndexSignature.ts, 18, 13))
61+
>indexed3 : Symbol(indexed3, Decl(objectSpreadIndexSignature.ts, 2, 11))
62+
>bar : Symbol(bar, Decl(objectSpreadIndexSignature.ts, 18, 37))
63+
64+
mixed.foo; // boolean
65+
>mixed.foo : Symbol(foo, Decl(objectSpreadIndexSignature.ts, 18, 13))
66+
>mixed : Symbol(mixed, Decl(objectSpreadIndexSignature.ts, 18, 3))
67+
>foo : Symbol(foo, Decl(objectSpreadIndexSignature.ts, 18, 13))
68+
69+
mixed.bar; // string
70+
>mixed.bar : Symbol(bar, Decl(objectSpreadIndexSignature.ts, 18, 37))
71+
>mixed : Symbol(mixed, Decl(objectSpreadIndexSignature.ts, 18, 3))
72+
>bar : Symbol(bar, Decl(objectSpreadIndexSignature.ts, 18, 37))
73+
74+
mixed.baz; // string | number | boolean
75+
>mixed : Symbol(mixed, Decl(objectSpreadIndexSignature.ts, 18, 3))
76+
77+
var mixed2 = { foo: true, ...indexed4, bar: 'bar' };
78+
>mixed2 : Symbol(mixed2, Decl(objectSpreadIndexSignature.ts, 23, 3))
79+
>foo : Symbol(foo, Decl(objectSpreadIndexSignature.ts, 23, 14))
80+
>indexed4 : Symbol(indexed4, Decl(objectSpreadIndexSignature.ts, 3, 11))
81+
>bar : Symbol(bar, Decl(objectSpreadIndexSignature.ts, 23, 38))
82+
83+
mixed2.foo; // boolean
84+
>mixed2.foo : Symbol(foo, Decl(objectSpreadIndexSignature.ts, 23, 14))
85+
>mixed2 : Symbol(mixed2, Decl(objectSpreadIndexSignature.ts, 23, 3))
86+
>foo : Symbol(foo, Decl(objectSpreadIndexSignature.ts, 23, 14))
87+
88+
mixed2.bar; // string
89+
>mixed2.bar : Symbol(bar, Decl(objectSpreadIndexSignature.ts, 23, 38))
90+
>mixed2 : Symbol(mixed2, Decl(objectSpreadIndexSignature.ts, 23, 3))
91+
>bar : Symbol(bar, Decl(objectSpreadIndexSignature.ts, 23, 38))
92+
93+
mixed2[1]; // number
94+
>mixed2 : Symbol(mixed2, Decl(objectSpreadIndexSignature.ts, 23, 3))
95+
96+
var mixed3 = { ...indexed4, '1': 'foo'};
97+
>mixed3 : Symbol(mixed3, Decl(objectSpreadIndexSignature.ts, 28, 3))
98+
>indexed4 : Symbol(indexed4, Decl(objectSpreadIndexSignature.ts, 3, 11))
99+
>'1' : Symbol('1', Decl(objectSpreadIndexSignature.ts, 28, 27))
100+
101+
mixed3[0]; // string | number
102+
>mixed3 : Symbol(mixed3, Decl(objectSpreadIndexSignature.ts, 28, 3))
103+
104+
mixed3[1]; // string
105+
>mixed3 : Symbol(mixed3, Decl(objectSpreadIndexSignature.ts, 28, 3))
106+
>1 : Symbol('1', Decl(objectSpreadIndexSignature.ts, 28, 27))
107+
108+
var mixed4 = { ...indexed3, '1': 'foo'};
109+
>mixed4 : Symbol(mixed4, Decl(objectSpreadIndexSignature.ts, 32, 3))
110+
>indexed3 : Symbol(indexed3, Decl(objectSpreadIndexSignature.ts, 2, 11))
111+
>'1' : Symbol('1', Decl(objectSpreadIndexSignature.ts, 32, 27))
112+
113+
mixed4.foo; // string | number
114+
>mixed4 : Symbol(mixed4, Decl(objectSpreadIndexSignature.ts, 32, 3))
115+
116+
mixed4[1]; // string
117+
>mixed4 : Symbol(mixed4, Decl(objectSpreadIndexSignature.ts, 32, 3))
118+
>1 : Symbol('1', Decl(objectSpreadIndexSignature.ts, 32, 27))
53119

tests/baselines/reference/objectSpreadIndexSignature.types

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,21 @@ declare let indexed3: { [n: string]: number };
1313
>indexed3 : { [n: string]: number; }
1414
>n : string
1515

16+
declare let indexed4: { [n: number]: number };
17+
>indexed4 : { [n: number]: number; }
18+
>n : number
19+
1620
let i = { ...indexed1, b: 11 };
17-
>i : { b: number; a: number; }
18-
>{ ...indexed1, b: 11 } : { b: number; a: number; }
21+
>i : { [x: string]: number; b: number; a: number; }
22+
>{ ...indexed1, b: 11 } : { [x: string]: number; b: number; a: number; }
1923
>indexed1 : { [n: string]: number; a: number; }
2024
>b : number
2125
>11 : 11
2226

2327
// only indexed has indexer, so i[101]: any
2428
i[101];
25-
>i[101] : any
26-
>i : { b: number; a: number; }
29+
>i[101] : number
30+
>i : { [x: string]: number; b: number; a: number; }
2731
>101 : 101
2832

2933
let ii = { ...indexed1, ...indexed2 };
@@ -42,9 +46,9 @@ declare const b: boolean;
4246
>b : boolean
4347

4448
indexed3 = { ...b ? indexed3 : undefined };
45-
>indexed3 = { ...b ? indexed3 : undefined } : {} | { [n: string]: number; }
49+
>indexed3 = { ...b ? indexed3 : undefined } : {} | { [x: string]: number; }
4650
>indexed3 : { [n: string]: number; }
47-
>{ ...b ? indexed3 : undefined } : {} | { [n: string]: number; }
51+
>{ ...b ? indexed3 : undefined } : {} | { [x: string]: number; }
4852
>b ? indexed3 : undefined : { [n: string]: number; } | undefined
4953
>b : boolean
5054
>indexed3 : { [n: string]: number; }
@@ -66,3 +70,85 @@ writable.a = 0; // should be ok.
6670
>a : number
6771
>0 : 0
6872

73+
var mixed = { foo: true, ...indexed3, bar: 'bar' };
74+
>mixed : { [x: string]: string | number | boolean; bar: string; foo: boolean; }
75+
>{ foo: true, ...indexed3, bar: 'bar' } : { [x: string]: string | number | boolean; bar: string; foo: boolean; }
76+
>foo : boolean
77+
>true : true
78+
>indexed3 : { [n: string]: number; }
79+
>bar : string
80+
>'bar' : "bar"
81+
82+
mixed.foo; // boolean
83+
>mixed.foo : boolean
84+
>mixed : { [x: string]: string | number | boolean; bar: string; foo: boolean; }
85+
>foo : boolean
86+
87+
mixed.bar; // string
88+
>mixed.bar : string
89+
>mixed : { [x: string]: string | number | boolean; bar: string; foo: boolean; }
90+
>bar : string
91+
92+
mixed.baz; // string | number | boolean
93+
>mixed.baz : string | number | boolean
94+
>mixed : { [x: string]: string | number | boolean; bar: string; foo: boolean; }
95+
>baz : string | number | boolean
96+
97+
var mixed2 = { foo: true, ...indexed4, bar: 'bar' };
98+
>mixed2 : { [x: number]: number; bar: string; foo: boolean; }
99+
>{ foo: true, ...indexed4, bar: 'bar' } : { [x: number]: number; bar: string; foo: boolean; }
100+
>foo : boolean
101+
>true : true
102+
>indexed4 : { [n: number]: number; }
103+
>bar : string
104+
>'bar' : "bar"
105+
106+
mixed2.foo; // boolean
107+
>mixed2.foo : boolean
108+
>mixed2 : { [x: number]: number; bar: string; foo: boolean; }
109+
>foo : boolean
110+
111+
mixed2.bar; // string
112+
>mixed2.bar : string
113+
>mixed2 : { [x: number]: number; bar: string; foo: boolean; }
114+
>bar : string
115+
116+
mixed2[1]; // number
117+
>mixed2[1] : number
118+
>mixed2 : { [x: number]: number; bar: string; foo: boolean; }
119+
>1 : 1
120+
121+
var mixed3 = { ...indexed4, '1': 'foo'};
122+
>mixed3 : { [x: number]: string | number; '1': string; }
123+
>{ ...indexed4, '1': 'foo'} : { [x: number]: string | number; '1': string; }
124+
>indexed4 : { [n: number]: number; }
125+
>'1' : string
126+
>'foo' : "foo"
127+
128+
mixed3[0]; // string | number
129+
>mixed3[0] : string | number
130+
>mixed3 : { [x: number]: string | number; '1': string; }
131+
>0 : 0
132+
133+
mixed3[1]; // string
134+
>mixed3[1] : string
135+
>mixed3 : { [x: number]: string | number; '1': string; }
136+
>1 : 1
137+
138+
var mixed4 = { ...indexed3, '1': 'foo'};
139+
>mixed4 : { [x: string]: string | number; '1': string; }
140+
>{ ...indexed3, '1': 'foo'} : { [x: string]: string | number; '1': string; }
141+
>indexed3 : { [n: string]: number; }
142+
>'1' : string
143+
>'foo' : "foo"
144+
145+
mixed4.foo; // string | number
146+
>mixed4.foo : string | number
147+
>mixed4 : { [x: string]: string | number; '1': string; }
148+
>foo : string | number
149+
150+
mixed4[1]; // string
151+
>mixed4[1] : string
152+
>mixed4 : { [x: string]: string | number; '1': string; }
153+
>1 : 1
154+

0 commit comments

Comments
 (0)