Skip to content

Commit 01de788

Browse files
authored
Avoid calculating union in spread if property types are identical (#53413)
1 parent 01a7a3e commit 01de788

8 files changed

+525
-1
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18186,7 +18186,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1818618186
const declarations = concatenate(leftProp.declarations, rightProp.declarations);
1818718187
const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional);
1818818188
const result = createSymbol(flags, leftProp.escapedName);
18189-
result.links.type = getUnionType([getTypeOfSymbol(leftProp), removeMissingOrUndefinedType(rightType)], UnionReduction.Subtype);
18189+
// Optimization: avoid calculating the union type if spreading into the exact same type.
18190+
// This is common, e.g. spreading one options bag into another where the bags have the
18191+
// same type, or have properties which overlap. If the unions are large, it may turn out
18192+
// to be expensive to perform subtype reduction.
18193+
const leftType = getTypeOfSymbol(leftProp);
18194+
const leftTypeWithoutUndefined = removeMissingOrUndefinedType(leftType);
18195+
const rightTypeWithoutUndefined = removeMissingOrUndefinedType(rightType);
18196+
result.links.type = leftTypeWithoutUndefined === rightTypeWithoutUndefined ? leftType : getUnionType([leftType, rightTypeWithoutUndefined], UnionReduction.Subtype);
1819018197
result.links.leftSpread = leftProp;
1819118198
result.links.rightSpread = rightProp;
1819218199
result.declarations = declarations;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//// [spreadObjectPermutations.ts]
2+
declare const a: { x: string | number };
3+
declare const b: { x?: string | number };
4+
declare const c: { x?: string | number | undefined };
5+
6+
const v_a = { ...a };
7+
const v_b = { ...b };
8+
const v_c = { ...c };
9+
const v_ab = { ...a, ...b };
10+
const v_ac = { ...a, ...c };
11+
const v_ba = { ...b, ...a };
12+
const v_bc = { ...b, ...c };
13+
const v_ca = { ...c, ...a };
14+
const v_cb = { ...c, ...b };
15+
const v_abc = { ...a, ...b, ...c };
16+
const v_acb = { ...a, ...c, ...b };
17+
const v_bac = { ...b, ...a, ...c };
18+
const v_bca = { ...b, ...c, ...a };
19+
const v_cab = { ...c, ...a, ...b };
20+
const v_cba = { ...c, ...b, ...a };
21+
22+
23+
//// [spreadObjectPermutations.js]
24+
"use strict";
25+
var __assign = (this && this.__assign) || function () {
26+
__assign = Object.assign || function(t) {
27+
for (var s, i = 1, n = arguments.length; i < n; i++) {
28+
s = arguments[i];
29+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
30+
t[p] = s[p];
31+
}
32+
return t;
33+
};
34+
return __assign.apply(this, arguments);
35+
};
36+
var v_a = __assign({}, a);
37+
var v_b = __assign({}, b);
38+
var v_c = __assign({}, c);
39+
var v_ab = __assign(__assign({}, a), b);
40+
var v_ac = __assign(__assign({}, a), c);
41+
var v_ba = __assign(__assign({}, b), a);
42+
var v_bc = __assign(__assign({}, b), c);
43+
var v_ca = __assign(__assign({}, c), a);
44+
var v_cb = __assign(__assign({}, c), b);
45+
var v_abc = __assign(__assign(__assign({}, a), b), c);
46+
var v_acb = __assign(__assign(__assign({}, a), c), b);
47+
var v_bac = __assign(__assign(__assign({}, b), a), c);
48+
var v_bca = __assign(__assign(__assign({}, b), c), a);
49+
var v_cab = __assign(__assign(__assign({}, c), a), b);
50+
var v_cba = __assign(__assign(__assign({}, c), b), a);
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
=== tests/cases/compiler/spreadObjectPermutations.ts ===
2+
declare const a: { x: string | number };
3+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
4+
>x : Symbol(x, Decl(spreadObjectPermutations.ts, 0, 18))
5+
6+
declare const b: { x?: string | number };
7+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
8+
>x : Symbol(x, Decl(spreadObjectPermutations.ts, 1, 18))
9+
10+
declare const c: { x?: string | number | undefined };
11+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
12+
>x : Symbol(x, Decl(spreadObjectPermutations.ts, 2, 18))
13+
14+
const v_a = { ...a };
15+
>v_a : Symbol(v_a, Decl(spreadObjectPermutations.ts, 4, 5))
16+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
17+
18+
const v_b = { ...b };
19+
>v_b : Symbol(v_b, Decl(spreadObjectPermutations.ts, 5, 5))
20+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
21+
22+
const v_c = { ...c };
23+
>v_c : Symbol(v_c, Decl(spreadObjectPermutations.ts, 6, 5))
24+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
25+
26+
const v_ab = { ...a, ...b };
27+
>v_ab : Symbol(v_ab, Decl(spreadObjectPermutations.ts, 7, 5))
28+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
29+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
30+
31+
const v_ac = { ...a, ...c };
32+
>v_ac : Symbol(v_ac, Decl(spreadObjectPermutations.ts, 8, 5))
33+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
34+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
35+
36+
const v_ba = { ...b, ...a };
37+
>v_ba : Symbol(v_ba, Decl(spreadObjectPermutations.ts, 9, 5))
38+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
39+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
40+
41+
const v_bc = { ...b, ...c };
42+
>v_bc : Symbol(v_bc, Decl(spreadObjectPermutations.ts, 10, 5))
43+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
44+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
45+
46+
const v_ca = { ...c, ...a };
47+
>v_ca : Symbol(v_ca, Decl(spreadObjectPermutations.ts, 11, 5))
48+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
49+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
50+
51+
const v_cb = { ...c, ...b };
52+
>v_cb : Symbol(v_cb, Decl(spreadObjectPermutations.ts, 12, 5))
53+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
54+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
55+
56+
const v_abc = { ...a, ...b, ...c };
57+
>v_abc : Symbol(v_abc, Decl(spreadObjectPermutations.ts, 13, 5))
58+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
59+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
60+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
61+
62+
const v_acb = { ...a, ...c, ...b };
63+
>v_acb : Symbol(v_acb, Decl(spreadObjectPermutations.ts, 14, 5))
64+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
65+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
66+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
67+
68+
const v_bac = { ...b, ...a, ...c };
69+
>v_bac : Symbol(v_bac, Decl(spreadObjectPermutations.ts, 15, 5))
70+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
71+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
72+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
73+
74+
const v_bca = { ...b, ...c, ...a };
75+
>v_bca : Symbol(v_bca, Decl(spreadObjectPermutations.ts, 16, 5))
76+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
77+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
78+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
79+
80+
const v_cab = { ...c, ...a, ...b };
81+
>v_cab : Symbol(v_cab, Decl(spreadObjectPermutations.ts, 17, 5))
82+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
83+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
84+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
85+
86+
const v_cba = { ...c, ...b, ...a };
87+
>v_cba : Symbol(v_cba, Decl(spreadObjectPermutations.ts, 18, 5))
88+
>c : Symbol(c, Decl(spreadObjectPermutations.ts, 2, 13))
89+
>b : Symbol(b, Decl(spreadObjectPermutations.ts, 1, 13))
90+
>a : Symbol(a, Decl(spreadObjectPermutations.ts, 0, 13))
91+
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
=== tests/cases/compiler/spreadObjectPermutations.ts ===
2+
declare const a: { x: string | number };
3+
>a : { x: string | number; }
4+
>x : string | number
5+
6+
declare const b: { x?: string | number };
7+
>b : { x?: string | number | undefined; }
8+
>x : string | number | undefined
9+
10+
declare const c: { x?: string | number | undefined };
11+
>c : { x?: string | number | undefined; }
12+
>x : string | number | undefined
13+
14+
const v_a = { ...a };
15+
>v_a : { x: string | number; }
16+
>{ ...a } : { x: string | number; }
17+
>a : { x: string | number; }
18+
19+
const v_b = { ...b };
20+
>v_b : { x?: string | number | undefined; }
21+
>{ ...b } : { x?: string | number | undefined; }
22+
>b : { x?: string | number | undefined; }
23+
24+
const v_c = { ...c };
25+
>v_c : { x?: string | number | undefined; }
26+
>{ ...c } : { x?: string | number | undefined; }
27+
>c : { x?: string | number | undefined; }
28+
29+
const v_ab = { ...a, ...b };
30+
>v_ab : { x: string | number; }
31+
>{ ...a, ...b } : { x: string | number; }
32+
>a : { x: string | number; }
33+
>b : { x?: string | number | undefined; }
34+
35+
const v_ac = { ...a, ...c };
36+
>v_ac : { x: string | number; }
37+
>{ ...a, ...c } : { x: string | number; }
38+
>a : { x: string | number; }
39+
>c : { x?: string | number | undefined; }
40+
41+
const v_ba = { ...b, ...a };
42+
>v_ba : { x: string | number; }
43+
>{ ...b, ...a } : { x: string | number; }
44+
>b : { x?: string | number | undefined; }
45+
>a : { x: string | number; }
46+
47+
const v_bc = { ...b, ...c };
48+
>v_bc : { x?: string | number | undefined; }
49+
>{ ...b, ...c } : { x?: string | number | undefined; }
50+
>b : { x?: string | number | undefined; }
51+
>c : { x?: string | number | undefined; }
52+
53+
const v_ca = { ...c, ...a };
54+
>v_ca : { x: string | number; }
55+
>{ ...c, ...a } : { x: string | number; }
56+
>c : { x?: string | number | undefined; }
57+
>a : { x: string | number; }
58+
59+
const v_cb = { ...c, ...b };
60+
>v_cb : { x?: string | number | undefined; }
61+
>{ ...c, ...b } : { x?: string | number | undefined; }
62+
>c : { x?: string | number | undefined; }
63+
>b : { x?: string | number | undefined; }
64+
65+
const v_abc = { ...a, ...b, ...c };
66+
>v_abc : { x: string | number; }
67+
>{ ...a, ...b, ...c } : { x: string | number; }
68+
>a : { x: string | number; }
69+
>b : { x?: string | number | undefined; }
70+
>c : { x?: string | number | undefined; }
71+
72+
const v_acb = { ...a, ...c, ...b };
73+
>v_acb : { x: string | number; }
74+
>{ ...a, ...c, ...b } : { x: string | number; }
75+
>a : { x: string | number; }
76+
>c : { x?: string | number | undefined; }
77+
>b : { x?: string | number | undefined; }
78+
79+
const v_bac = { ...b, ...a, ...c };
80+
>v_bac : { x: string | number; }
81+
>{ ...b, ...a, ...c } : { x: string | number; }
82+
>b : { x?: string | number | undefined; }
83+
>a : { x: string | number; }
84+
>c : { x?: string | number | undefined; }
85+
86+
const v_bca = { ...b, ...c, ...a };
87+
>v_bca : { x: string | number; }
88+
>{ ...b, ...c, ...a } : { x: string | number; }
89+
>b : { x?: string | number | undefined; }
90+
>c : { x?: string | number | undefined; }
91+
>a : { x: string | number; }
92+
93+
const v_cab = { ...c, ...a, ...b };
94+
>v_cab : { x: string | number; }
95+
>{ ...c, ...a, ...b } : { x: string | number; }
96+
>c : { x?: string | number | undefined; }
97+
>a : { x: string | number; }
98+
>b : { x?: string | number | undefined; }
99+
100+
const v_cba = { ...c, ...b, ...a };
101+
>v_cba : { x: string | number; }
102+
>{ ...c, ...b, ...a } : { x: string | number; }
103+
>c : { x?: string | number | undefined; }
104+
>b : { x?: string | number | undefined; }
105+
>a : { x: string | number; }
106+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//// [spreadObjectPermutations.ts]
2+
declare const a: { x: string | number };
3+
declare const b: { x?: string | number };
4+
declare const c: { x?: string | number | undefined };
5+
6+
const v_a = { ...a };
7+
const v_b = { ...b };
8+
const v_c = { ...c };
9+
const v_ab = { ...a, ...b };
10+
const v_ac = { ...a, ...c };
11+
const v_ba = { ...b, ...a };
12+
const v_bc = { ...b, ...c };
13+
const v_ca = { ...c, ...a };
14+
const v_cb = { ...c, ...b };
15+
const v_abc = { ...a, ...b, ...c };
16+
const v_acb = { ...a, ...c, ...b };
17+
const v_bac = { ...b, ...a, ...c };
18+
const v_bca = { ...b, ...c, ...a };
19+
const v_cab = { ...c, ...a, ...b };
20+
const v_cba = { ...c, ...b, ...a };
21+
22+
23+
//// [spreadObjectPermutations.js]
24+
"use strict";
25+
var __assign = (this && this.__assign) || function () {
26+
__assign = Object.assign || function(t) {
27+
for (var s, i = 1, n = arguments.length; i < n; i++) {
28+
s = arguments[i];
29+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
30+
t[p] = s[p];
31+
}
32+
return t;
33+
};
34+
return __assign.apply(this, arguments);
35+
};
36+
var v_a = __assign({}, a);
37+
var v_b = __assign({}, b);
38+
var v_c = __assign({}, c);
39+
var v_ab = __assign(__assign({}, a), b);
40+
var v_ac = __assign(__assign({}, a), c);
41+
var v_ba = __assign(__assign({}, b), a);
42+
var v_bc = __assign(__assign({}, b), c);
43+
var v_ca = __assign(__assign({}, c), a);
44+
var v_cb = __assign(__assign({}, c), b);
45+
var v_abc = __assign(__assign(__assign({}, a), b), c);
46+
var v_acb = __assign(__assign(__assign({}, a), c), b);
47+
var v_bac = __assign(__assign(__assign({}, b), a), c);
48+
var v_bca = __assign(__assign(__assign({}, b), c), a);
49+
var v_cab = __assign(__assign(__assign({}, c), a), b);
50+
var v_cba = __assign(__assign(__assign({}, c), b), a);

0 commit comments

Comments
 (0)