Skip to content

Commit 11606e4

Browse files
authored
Do for unions of many empty-object-spreadables what we did for unions of 2 (#42233)
* Do for unions of many empty-object-spreadables what we did for unions of 2 * Accept baseline
1 parent dbba8b3 commit 11606e4

15 files changed

+281
-58
lines changed

src/compiler/checker.ts

+12-13
Original file line numberDiff line numberDiff line change
@@ -14670,19 +14670,18 @@ namespace ts {
1467014670
}
1467114671

1467214672
function tryMergeUnionOfObjectTypeAndEmptyObject(type: UnionType, readonly: boolean): Type | undefined {
14673-
if (type.types.length === 2) {
14674-
const firstType = type.types[0];
14675-
const secondType = type.types[1];
14676-
if (every(type.types, isEmptyObjectTypeOrSpreadsIntoEmptyObject)) {
14677-
return isEmptyObjectType(firstType) ? firstType : isEmptyObjectType(secondType) ? secondType : emptyObjectType;
14678-
}
14679-
if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(firstType)) {
14680-
return getAnonymousPartialType(secondType);
14681-
}
14682-
if (isEmptyObjectTypeOrSpreadsIntoEmptyObject(secondType)) {
14683-
return getAnonymousPartialType(firstType);
14684-
}
14673+
if (every(type.types, isEmptyObjectTypeOrSpreadsIntoEmptyObject)) {
14674+
return find(type.types, isEmptyObjectType) || emptyObjectType;
14675+
}
14676+
const firstType = find(type.types, t => !isEmptyObjectTypeOrSpreadsIntoEmptyObject(t));
14677+
if (!firstType) {
14678+
return undefined;
14679+
}
14680+
const secondType = firstType && find(type.types, t => t !== firstType && !isEmptyObjectTypeOrSpreadsIntoEmptyObject(t));
14681+
if (secondType) {
14682+
return undefined;
1468514683
}
14684+
return getAnonymousPartialType(firstType);
1468614685

1468714686
function getAnonymousPartialType(type: Type) {
1468814687
// gets the type as if it had been spread, but where everything in the spread is made optional
@@ -14695,7 +14694,7 @@ namespace ts {
1469514694
const isSetonlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor);
1469614695
const flags = SymbolFlags.Property | SymbolFlags.Optional;
1469714696
const result = createSymbol(flags, prop.escapedName, getIsLateCheckFlag(prop) | (readonly ? CheckFlags.Readonly : 0));
14698-
result.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop);
14697+
result.type = isSetonlyAccessor ? undefinedType : getUnionType([getTypeOfSymbol(prop), undefinedType]);
1469914698
result.declarations = prop.declarations;
1470014699
result.nameType = getSymbolLinks(prop).nameType;
1470114700
result.syntheticOrigin = prop;

tests/baselines/reference/objectSpread.types

+6-6
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ function conditionalSpreadBoolean(b: boolean) : { x: number, y: number } {
229229
>14 : 14
230230
}
231231
let o2 = { ...b && { x: 21 }}
232-
>o2 : { x?: number; }
233-
>{ ...b && { x: 21 }} : { x?: number; }
232+
>o2 : { x?: number | undefined; }
233+
>{ ...b && { x: 21 }} : { x?: number | undefined; }
234234
>b && { x: 21 } : false | { x: number; }
235235
>b : boolean
236236
>{ x: 21 } : { x: number; }
@@ -270,8 +270,8 @@ function conditionalSpreadNumber(nt: number): { x: number, y: number } {
270270
>nt : number
271271
}
272272
let o2 = { ...nt && { x: nt }}
273-
>o2 : { x?: number; }
274-
>{ ...nt && { x: nt }} : { x?: number; }
273+
>o2 : { x?: number | undefined; }
274+
>{ ...nt && { x: nt }} : { x?: number | undefined; }
275275
>nt && { x: nt } : 0 | { x: number; }
276276
>nt : number
277277
>{ x: nt } : { x: number; }
@@ -311,8 +311,8 @@ function conditionalSpreadString(st: string): { x: string, y: number } {
311311
>st : string
312312
}
313313
let o2 = { ...st && { x: st }}
314-
>o2 : { x?: string; }
315-
>{ ...st && { x: st }} : { x?: string; }
314+
>o2 : { x?: string | undefined; }
315+
>{ ...st && { x: st }} : { x?: string | undefined; }
316316
>st && { x: st } : "" | { x: string; }
317317
>st : string
318318
>{ x: st } : { x: string; }

tests/baselines/reference/objectSpreadRepeatedComplexity.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
=== tests/cases/conformance/types/spread/objectSpreadRepeatedComplexity.ts ===
22
function f(cnd: Record<number, boolean>){
3-
>f : (cnd: Record<number, boolean>) => { prop20a?: number; prop20b?: number; prop19a?: number; prop19b?: number; prop18a?: number; prop18b?: number; prop17a?: number; prop17b?: number; prop16a?: number; prop16b?: number; prop15a?: number; prop15b?: number; prop14a?: number; prop14b?: number; prop13a?: number; prop13b?: number; prop12a?: number; prop12b?: number; prop11a?: number; prop11b?: number; prop10a?: number; prop10b?: number; prop9a?: number; prop9b?: number; prop8a?: number; prop8b?: number; prop7a?: number; prop7b?: number; prop6a?: number; prop6b?: number; prop5a?: number; prop5b?: number; prop4a?: number; prop4b?: number; prop3a?: number; prop3b?: number; prop0?: number; }
3+
>f : (cnd: Record<number, boolean>) => { prop20a?: number | undefined; prop20b?: number | undefined; prop19a?: number | undefined; prop19b?: number | undefined; prop18a?: number | undefined; prop18b?: number | undefined; prop17a?: number | undefined; prop17b?: number | undefined; prop16a?: number | undefined; prop16b?: number | undefined; prop15a?: number | undefined; prop15b?: number | undefined; prop14a?: number | undefined; prop14b?: number | undefined; prop13a?: number | undefined; prop13b?: number | undefined; prop12a?: number | undefined; prop12b?: number | undefined; prop11a?: number | undefined; prop11b?: number | undefined; prop10a?: number | undefined; prop10b?: number | undefined; prop9a?: number | undefined; prop9b?: number | undefined; prop8a?: number | undefined; prop8b?: number | undefined; prop7a?: number | undefined; prop7b?: number | undefined; prop6a?: number | undefined; prop6b?: number | undefined; prop5a?: number | undefined; prop5b?: number | undefined; prop4a?: number | undefined; prop4b?: number | undefined; prop3a?: number | undefined; prop3b?: number | undefined; prop0?: number | undefined; }
44
>cnd : Record<number, boolean>
55

66
// Type is a union of 2^(n-1) members, where n is the number of spread objects
77
return {
8-
>{ // Without this one, it collapses to {} ? ...(cnd[1] && cnd[2] && { prop0: 0, }), // With one prop each, it collapses to a single object (#34853?) ...(cnd[3] && { prop3a: 1, prop3b: 1, }), ...(cnd[4] && { prop4a: 1, prop4b: 1, }), ...(cnd[5] && { prop5a: 1, prop5b: 1, }), ...(cnd[6] && { prop6a: 1, prop6b: 1, }), ...(cnd[7] && { prop7a: 1, prop7b: 1, }), ...(cnd[8] && { prop8a: 1, prop8b: 1, }), ...(cnd[9] && { prop9a: 1, prop9b: 1, }), ...(cnd[10] && { prop10a: 1, prop10b: 1, }), ...(cnd[11] && { prop11a: 1, prop11b: 1, }), ...(cnd[12] && { prop12a: 1, prop12b: 1, }), ...(cnd[13] && { prop13a: 1, prop13b: 1, }), ...(cnd[14] && { prop14a: 1, prop14b: 1, }), ...(cnd[15] && { prop15a: 1, prop15b: 1, }), ...(cnd[16] && { prop16a: 1, prop16b: 1, }), ...(cnd[17] && { prop17a: 1, prop17b: 1, }), ...(cnd[18] && { prop18a: 1, prop18b: 1, }), ...(cnd[19] && { prop19a: 1, prop19b: 1, }), ...(cnd[20] && { prop20a: 1, prop20b: 1, }), } : { prop20a?: number; prop20b?: number; prop19a?: number; prop19b?: number; prop18a?: number; prop18b?: number; prop17a?: number; prop17b?: number; prop16a?: number; prop16b?: number; prop15a?: number; prop15b?: number; prop14a?: number; prop14b?: number; prop13a?: number; prop13b?: number; prop12a?: number; prop12b?: number; prop11a?: number; prop11b?: number; prop10a?: number; prop10b?: number; prop9a?: number; prop9b?: number; prop8a?: number; prop8b?: number; prop7a?: number; prop7b?: number; prop6a?: number; prop6b?: number; prop5a?: number; prop5b?: number; prop4a?: number; prop4b?: number; prop3a?: number; prop3b?: number; prop0?: number; }
8+
>{ // Without this one, it collapses to {} ? ...(cnd[1] && cnd[2] && { prop0: 0, }), // With one prop each, it collapses to a single object (#34853?) ...(cnd[3] && { prop3a: 1, prop3b: 1, }), ...(cnd[4] && { prop4a: 1, prop4b: 1, }), ...(cnd[5] && { prop5a: 1, prop5b: 1, }), ...(cnd[6] && { prop6a: 1, prop6b: 1, }), ...(cnd[7] && { prop7a: 1, prop7b: 1, }), ...(cnd[8] && { prop8a: 1, prop8b: 1, }), ...(cnd[9] && { prop9a: 1, prop9b: 1, }), ...(cnd[10] && { prop10a: 1, prop10b: 1, }), ...(cnd[11] && { prop11a: 1, prop11b: 1, }), ...(cnd[12] && { prop12a: 1, prop12b: 1, }), ...(cnd[13] && { prop13a: 1, prop13b: 1, }), ...(cnd[14] && { prop14a: 1, prop14b: 1, }), ...(cnd[15] && { prop15a: 1, prop15b: 1, }), ...(cnd[16] && { prop16a: 1, prop16b: 1, }), ...(cnd[17] && { prop17a: 1, prop17b: 1, }), ...(cnd[18] && { prop18a: 1, prop18b: 1, }), ...(cnd[19] && { prop19a: 1, prop19b: 1, }), ...(cnd[20] && { prop20a: 1, prop20b: 1, }), } : { prop20a?: number | undefined; prop20b?: number | undefined; prop19a?: number | undefined; prop19b?: number | undefined; prop18a?: number | undefined; prop18b?: number | undefined; prop17a?: number | undefined; prop17b?: number | undefined; prop16a?: number | undefined; prop16b?: number | undefined; prop15a?: number | undefined; prop15b?: number | undefined; prop14a?: number | undefined; prop14b?: number | undefined; prop13a?: number | undefined; prop13b?: number | undefined; prop12a?: number | undefined; prop12b?: number | undefined; prop11a?: number | undefined; prop11b?: number | undefined; prop10a?: number | undefined; prop10b?: number | undefined; prop9a?: number | undefined; prop9b?: number | undefined; prop8a?: number | undefined; prop8b?: number | undefined; prop7a?: number | undefined; prop7b?: number | undefined; prop6a?: number | undefined; prop6b?: number | undefined; prop5a?: number | undefined; prop5b?: number | undefined; prop4a?: number | undefined; prop4b?: number | undefined; prop3a?: number | undefined; prop3b?: number | undefined; prop0?: number | undefined; }
99

1010
// Without this one, it collapses to {} ?
1111
...(cnd[1] &&

tests/baselines/reference/objectSpreadRepeatedNullCheckPerf.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ function parseWithSpread(config: Record<string, number>): Props {
8484
>config : Record<string, number>
8585

8686
return {
87-
>{ ...config.a !== undefined && { a: config.a.toString() }, ...config.b !== undefined && { b: config.b.toString() }, ...config.c !== undefined && { c: config.c.toString() }, ...config.d !== undefined && { d: config.d.toString() }, ...config.e !== undefined && { e: config.e.toString() }, ...config.f !== undefined && { f: config.f.toString() }, ...config.g !== undefined && { g: config.g.toString() }, ...config.h !== undefined && { h: config.h.toString() }, ...config.i !== undefined && { i: config.i.toString() }, ...config.j !== undefined && { j: config.j.toString() }, ...config.k !== undefined && { k: config.k.toString() }, ...config.l !== undefined && { l: config.l.toString() }, ...config.m !== undefined && { m: config.m.toString() }, ...config.n !== undefined && { n: config.n.toString() }, ...config.o !== undefined && { o: config.o.toString() }, ...config.p !== undefined && { p: config.p.toString() }, ...config.q !== undefined && { q: config.q.toString() }, ...config.r !== undefined && { r: config.r.toString() }, ...config.s !== undefined && { s: config.s.toString() }, ...config.t !== undefined && { t: config.t.toString() }, ...config.u !== undefined && { u: config.u.toString() }, ...config.v !== undefined && { v: config.v.toString() }, ...config.w !== undefined && { w: config.w.toString() }, ...config.x !== undefined && { x: config.x.toString() }, ...config.y !== undefined && { y: config.y.toString() }, ...config.z !== undefined && { z: config.z.toString() } } : { z?: string; y?: string; x?: string; w?: string; v?: string; u?: string; t?: string; s?: string; r?: string; q?: string; p?: string; o?: string; n?: string; m?: string; l?: string; k?: string; j?: string; i?: string; h?: string; g?: string; f?: string; e?: string; d?: string; c?: string; b?: string; a?: string; }
87+
>{ ...config.a !== undefined && { a: config.a.toString() }, ...config.b !== undefined && { b: config.b.toString() }, ...config.c !== undefined && { c: config.c.toString() }, ...config.d !== undefined && { d: config.d.toString() }, ...config.e !== undefined && { e: config.e.toString() }, ...config.f !== undefined && { f: config.f.toString() }, ...config.g !== undefined && { g: config.g.toString() }, ...config.h !== undefined && { h: config.h.toString() }, ...config.i !== undefined && { i: config.i.toString() }, ...config.j !== undefined && { j: config.j.toString() }, ...config.k !== undefined && { k: config.k.toString() }, ...config.l !== undefined && { l: config.l.toString() }, ...config.m !== undefined && { m: config.m.toString() }, ...config.n !== undefined && { n: config.n.toString() }, ...config.o !== undefined && { o: config.o.toString() }, ...config.p !== undefined && { p: config.p.toString() }, ...config.q !== undefined && { q: config.q.toString() }, ...config.r !== undefined && { r: config.r.toString() }, ...config.s !== undefined && { s: config.s.toString() }, ...config.t !== undefined && { t: config.t.toString() }, ...config.u !== undefined && { u: config.u.toString() }, ...config.v !== undefined && { v: config.v.toString() }, ...config.w !== undefined && { w: config.w.toString() }, ...config.x !== undefined && { x: config.x.toString() }, ...config.y !== undefined && { y: config.y.toString() }, ...config.z !== undefined && { z: config.z.toString() } } : { z?: string | undefined; y?: string | undefined; x?: string | undefined; w?: string | undefined; v?: string | undefined; u?: string | undefined; t?: string | undefined; s?: string | undefined; r?: string | undefined; q?: string | undefined; p?: string | undefined; o?: string | undefined; n?: string | undefined; m?: string | undefined; l?: string | undefined; k?: string | undefined; j?: string | undefined; i?: string | undefined; h?: string | undefined; g?: string | undefined; f?: string | undefined; e?: string | undefined; d?: string | undefined; c?: string | undefined; b?: string | undefined; a?: string | undefined; }
8888

8989
...config.a !== undefined && { a: config.a.toString() },
9090
>config.a !== undefined && { a: config.a.toString() } : false | { a: string; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//// [spreadIdenticalTypesRemoved.ts]
2+
interface Animal {
3+
name: string;
4+
kind: string;
5+
age: number;
6+
location: string;
7+
owner: object;
8+
}
9+
10+
function clonePet(pet: Animal, fullCopy?: boolean) {
11+
return {
12+
name: pet.name,
13+
kind: pet.kind,
14+
...(fullCopy && pet),
15+
}
16+
}
17+
18+
interface Animal2 {
19+
name: string;
20+
owner?: string;
21+
}
22+
function billOwner(pet: Animal2) {
23+
return {
24+
...(pet.owner && pet),
25+
paid: false
26+
}
27+
}
28+
29+
30+
//// [spreadIdenticalTypesRemoved.js]
31+
"use strict";
32+
var __assign = (this && this.__assign) || function () {
33+
__assign = Object.assign || function(t) {
34+
for (var s, i = 1, n = arguments.length; i < n; i++) {
35+
s = arguments[i];
36+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
37+
t[p] = s[p];
38+
}
39+
return t;
40+
};
41+
return __assign.apply(this, arguments);
42+
};
43+
function clonePet(pet, fullCopy) {
44+
return __assign({ name: pet.name, kind: pet.kind }, (fullCopy && pet));
45+
}
46+
function billOwner(pet) {
47+
return __assign(__assign({}, (pet.owner && pet)), { paid: false });
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
=== tests/cases/compiler/spreadIdenticalTypesRemoved.ts ===
2+
interface Animal {
3+
>Animal : Symbol(Animal, Decl(spreadIdenticalTypesRemoved.ts, 0, 0))
4+
5+
name: string;
6+
>name : Symbol(Animal.name, Decl(spreadIdenticalTypesRemoved.ts, 0, 18))
7+
8+
kind: string;
9+
>kind : Symbol(Animal.kind, Decl(spreadIdenticalTypesRemoved.ts, 1, 17))
10+
11+
age: number;
12+
>age : Symbol(Animal.age, Decl(spreadIdenticalTypesRemoved.ts, 2, 17))
13+
14+
location: string;
15+
>location : Symbol(Animal.location, Decl(spreadIdenticalTypesRemoved.ts, 3, 16))
16+
17+
owner: object;
18+
>owner : Symbol(Animal.owner, Decl(spreadIdenticalTypesRemoved.ts, 4, 21))
19+
}
20+
21+
function clonePet(pet: Animal, fullCopy?: boolean) {
22+
>clonePet : Symbol(clonePet, Decl(spreadIdenticalTypesRemoved.ts, 6, 1))
23+
>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 8, 18))
24+
>Animal : Symbol(Animal, Decl(spreadIdenticalTypesRemoved.ts, 0, 0))
25+
>fullCopy : Symbol(fullCopy, Decl(spreadIdenticalTypesRemoved.ts, 8, 30))
26+
27+
return {
28+
name: pet.name,
29+
>name : Symbol(name, Decl(spreadIdenticalTypesRemoved.ts, 9, 12))
30+
>pet.name : Symbol(Animal.name, Decl(spreadIdenticalTypesRemoved.ts, 0, 18))
31+
>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 8, 18))
32+
>name : Symbol(Animal.name, Decl(spreadIdenticalTypesRemoved.ts, 0, 18))
33+
34+
kind: pet.kind,
35+
>kind : Symbol(kind, Decl(spreadIdenticalTypesRemoved.ts, 10, 23))
36+
>pet.kind : Symbol(Animal.kind, Decl(spreadIdenticalTypesRemoved.ts, 1, 17))
37+
>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 8, 18))
38+
>kind : Symbol(Animal.kind, Decl(spreadIdenticalTypesRemoved.ts, 1, 17))
39+
40+
...(fullCopy && pet),
41+
>fullCopy : Symbol(fullCopy, Decl(spreadIdenticalTypesRemoved.ts, 8, 30))
42+
>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 8, 18))
43+
}
44+
}
45+
46+
interface Animal2 {
47+
>Animal2 : Symbol(Animal2, Decl(spreadIdenticalTypesRemoved.ts, 14, 1))
48+
49+
name: string;
50+
>name : Symbol(Animal2.name, Decl(spreadIdenticalTypesRemoved.ts, 16, 19))
51+
52+
owner?: string;
53+
>owner : Symbol(Animal2.owner, Decl(spreadIdenticalTypesRemoved.ts, 17, 17))
54+
}
55+
function billOwner(pet: Animal2) {
56+
>billOwner : Symbol(billOwner, Decl(spreadIdenticalTypesRemoved.ts, 19, 1))
57+
>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 20, 19))
58+
>Animal2 : Symbol(Animal2, Decl(spreadIdenticalTypesRemoved.ts, 14, 1))
59+
60+
return {
61+
...(pet.owner && pet),
62+
>pet.owner : Symbol(Animal2.owner, Decl(spreadIdenticalTypesRemoved.ts, 17, 17))
63+
>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 20, 19))
64+
>owner : Symbol(Animal2.owner, Decl(spreadIdenticalTypesRemoved.ts, 17, 17))
65+
>pet : Symbol(pet, Decl(spreadIdenticalTypesRemoved.ts, 20, 19))
66+
67+
paid: false
68+
>paid : Symbol(paid, Decl(spreadIdenticalTypesRemoved.ts, 22, 30))
69+
}
70+
}
71+

0 commit comments

Comments
 (0)