Skip to content

Commit 8e4b90d

Browse files
authored
Merge pull request #28234 from Microsoft/genericSpread
Generic spread expressions in object literals
2 parents 6fd6a04 + 1577f94 commit 8e4b90d

15 files changed

+582
-319
lines changed

Diff for: src/compiler/checker.ts

+23-3
Original file line numberDiff line numberDiff line change
@@ -4639,7 +4639,7 @@ namespace ts {
46394639
let type: Type | undefined;
46404640
if (pattern.kind === SyntaxKind.ObjectBindingPattern) {
46414641
if (declaration.dotDotDotToken) {
4642-
if (parentType.flags & TypeFlags.Unknown || !isValidSpreadType(parentType)) {
4642+
if (parentType.flags & TypeFlags.Unknown || !isValidSpreadType(parentType) || isGenericObjectType(parentType)) {
46434643
error(declaration, Diagnostics.Rest_types_may_only_be_created_from_object_types);
46444644
return errorType;
46454645
}
@@ -9842,6 +9842,10 @@ namespace ts {
98429842
return symbol ? getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) : undefined;
98439843
}
98449844

9845+
function isNonGenericObjectType(type: Type) {
9846+
return !!(type.flags & TypeFlags.Object) && !isGenericMappedType(type);
9847+
}
9848+
98459849
/**
98469850
* Since the source of spread types are object literals, which are not binary,
98479851
* this function should be called in a left folding style, with left = previous result of getSpreadType
@@ -9870,6 +9874,23 @@ namespace ts {
98709874
return left;
98719875
}
98729876

9877+
if (isGenericObjectType(left) || isGenericObjectType(right)) {
9878+
if (isEmptyObjectType(left)) {
9879+
return right;
9880+
}
9881+
// When the left type is an intersection, we may need to merge the last constituent of the
9882+
// intersection with the right type. For example when the left type is 'T & { a: string }'
9883+
// and the right type is '{ b: string }' we produce 'T & { a: string, b: string }'.
9884+
if (left.flags & TypeFlags.Intersection) {
9885+
const types = (<IntersectionType>left).types;
9886+
const lastLeft = types[types.length - 1];
9887+
if (isNonGenericObjectType(lastLeft) && isNonGenericObjectType(right)) {
9888+
return getIntersectionType(concatenate(types.slice(0, types.length - 1), [getSpreadType(lastLeft, right, symbol, typeFlags, objectFlags)]));
9889+
}
9890+
}
9891+
return getIntersectionType([left, right]);
9892+
}
9893+
98739894
const members = createSymbolTable();
98749895
const skippedPrivateMembers = createUnderscoreEscapedMap<boolean>();
98759896
let stringIndexInfo: IndexInfo | undefined;
@@ -17700,9 +17721,8 @@ namespace ts {
1770017721
}
1770117722

1770217723
function isValidSpreadType(type: Type): boolean {
17703-
return !!(type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.NonPrimitive) ||
17724+
return !!(type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.NonPrimitive | TypeFlags.Object | TypeFlags.InstantiableNonPrimitive) ||
1770417725
getFalsyFlags(type) & TypeFlags.DefinitelyFalsy && isValidSpreadType(removeDefinitelyFalsyTypes(type)) ||
17705-
type.flags & TypeFlags.Object && !isGenericMappedType(type) ||
1770617726
type.flags & TypeFlags.UnionOrIntersection && every((<UnionOrIntersectionType>type).types, isValidSpreadType));
1770717727
}
1770817728

Diff for: tests/baselines/reference/objectSpread.js

+62
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,41 @@ let a = 12;
119119
let shortCutted: { a: number, b: string } = { ...o, a }
120120
// non primitive
121121
let spreadNonPrimitive = { ...<object>{}};
122+
123+
// generic spreads
124+
125+
function f<T, U>(t: T, u: U) {
126+
return { ...t, ...u, id: 'id' };
127+
}
128+
129+
let exclusive: { id: string, a: number, b: string, c: string, d: boolean } =
130+
f({ a: 1, b: 'yes' }, { c: 'no', d: false })
131+
let overlap: { id: string, a: number, b: string } =
132+
f({ a: 1 }, { a: 2, b: 'extra' })
133+
let overlapConflict: { id:string, a: string } =
134+
f({ a: 1 }, { a: 'mismatch' })
135+
let overwriteId: { id: string, a: number, c: number, d: string } =
136+
f({ a: 1, id: true }, { c: 1, d: 'no' })
137+
138+
function genericSpread<T, U>(t: T, u: U, v: T | U, w: T | { s: string }, obj: { x: number }) {
139+
let x01 = { ...t };
140+
let x02 = { ...t, ...t };
141+
let x03 = { ...t, ...u };
142+
let x04 = { ...u, ...t };
143+
let x05 = { a: 5, b: 'hi', ...t };
144+
let x06 = { ...t, a: 5, b: 'hi' };
145+
let x07 = { a: 5, b: 'hi', ...t, c: true, ...obj };
146+
let x09 = { a: 5, ...t, b: 'hi', c: true, ...obj };
147+
let x10 = { a: 5, ...t, b: 'hi', ...u, ...obj };
148+
let x11 = { ...v };
149+
let x12 = { ...v, ...obj };
150+
let x13 = { ...w };
151+
let x14 = { ...w, ...obj };
152+
let x15 = { ...t, ...v };
153+
let x16 = { ...t, ...w };
154+
let x17 = { ...t, ...w, ...obj };
155+
let x18 = { ...t, ...v, ...w };
156+
}
122157

123158

124159
//// [objectSpread.js]
@@ -214,3 +249,30 @@ var a = 12;
214249
var shortCutted = __assign({}, o, { a: a });
215250
// non primitive
216251
var spreadNonPrimitive = __assign({}, {});
252+
// generic spreads
253+
function f(t, u) {
254+
return __assign({}, t, u, { id: 'id' });
255+
}
256+
var exclusive = f({ a: 1, b: 'yes' }, { c: 'no', d: false });
257+
var overlap = f({ a: 1 }, { a: 2, b: 'extra' });
258+
var overlapConflict = f({ a: 1 }, { a: 'mismatch' });
259+
var overwriteId = f({ a: 1, id: true }, { c: 1, d: 'no' });
260+
function genericSpread(t, u, v, w, obj) {
261+
var x01 = __assign({}, t);
262+
var x02 = __assign({}, t, t);
263+
var x03 = __assign({}, t, u);
264+
var x04 = __assign({}, u, t);
265+
var x05 = __assign({ a: 5, b: 'hi' }, t);
266+
var x06 = __assign({}, t, { a: 5, b: 'hi' });
267+
var x07 = __assign({ a: 5, b: 'hi' }, t, { c: true }, obj);
268+
var x09 = __assign({ a: 5 }, t, { b: 'hi', c: true }, obj);
269+
var x10 = __assign({ a: 5 }, t, { b: 'hi' }, u, obj);
270+
var x11 = __assign({}, v);
271+
var x12 = __assign({}, v, obj);
272+
var x13 = __assign({}, w);
273+
var x14 = __assign({}, w, obj);
274+
var x15 = __assign({}, t, v);
275+
var x16 = __assign({}, t, w);
276+
var x17 = __assign({}, t, w, obj);
277+
var x18 = __assign({}, t, v, w);
278+
}

Diff for: tests/baselines/reference/objectSpread.symbols

+181
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,184 @@ let shortCutted: { a: number, b: string } = { ...o, a }
452452
let spreadNonPrimitive = { ...<object>{}};
453453
>spreadNonPrimitive : Symbol(spreadNonPrimitive, Decl(objectSpread.ts, 119, 3))
454454

455+
// generic spreads
456+
457+
function f<T, U>(t: T, u: U) {
458+
>f : Symbol(f, Decl(objectSpread.ts, 119, 42))
459+
>T : Symbol(T, Decl(objectSpread.ts, 123, 11))
460+
>U : Symbol(U, Decl(objectSpread.ts, 123, 13))
461+
>t : Symbol(t, Decl(objectSpread.ts, 123, 17))
462+
>T : Symbol(T, Decl(objectSpread.ts, 123, 11))
463+
>u : Symbol(u, Decl(objectSpread.ts, 123, 22))
464+
>U : Symbol(U, Decl(objectSpread.ts, 123, 13))
465+
466+
return { ...t, ...u, id: 'id' };
467+
>t : Symbol(t, Decl(objectSpread.ts, 123, 17))
468+
>u : Symbol(u, Decl(objectSpread.ts, 123, 22))
469+
>id : Symbol(id, Decl(objectSpread.ts, 124, 24))
470+
}
471+
472+
let exclusive: { id: string, a: number, b: string, c: string, d: boolean } =
473+
>exclusive : Symbol(exclusive, Decl(objectSpread.ts, 127, 3))
474+
>id : Symbol(id, Decl(objectSpread.ts, 127, 16))
475+
>a : Symbol(a, Decl(objectSpread.ts, 127, 28))
476+
>b : Symbol(b, Decl(objectSpread.ts, 127, 39))
477+
>c : Symbol(c, Decl(objectSpread.ts, 127, 50))
478+
>d : Symbol(d, Decl(objectSpread.ts, 127, 61))
479+
480+
f({ a: 1, b: 'yes' }, { c: 'no', d: false })
481+
>f : Symbol(f, Decl(objectSpread.ts, 119, 42))
482+
>a : Symbol(a, Decl(objectSpread.ts, 128, 7))
483+
>b : Symbol(b, Decl(objectSpread.ts, 128, 13))
484+
>c : Symbol(c, Decl(objectSpread.ts, 128, 27))
485+
>d : Symbol(d, Decl(objectSpread.ts, 128, 36))
486+
487+
let overlap: { id: string, a: number, b: string } =
488+
>overlap : Symbol(overlap, Decl(objectSpread.ts, 129, 3))
489+
>id : Symbol(id, Decl(objectSpread.ts, 129, 14))
490+
>a : Symbol(a, Decl(objectSpread.ts, 129, 26))
491+
>b : Symbol(b, Decl(objectSpread.ts, 129, 37))
492+
493+
f({ a: 1 }, { a: 2, b: 'extra' })
494+
>f : Symbol(f, Decl(objectSpread.ts, 119, 42))
495+
>a : Symbol(a, Decl(objectSpread.ts, 130, 7))
496+
>a : Symbol(a, Decl(objectSpread.ts, 130, 17))
497+
>b : Symbol(b, Decl(objectSpread.ts, 130, 23))
498+
499+
let overlapConflict: { id:string, a: string } =
500+
>overlapConflict : Symbol(overlapConflict, Decl(objectSpread.ts, 131, 3))
501+
>id : Symbol(id, Decl(objectSpread.ts, 131, 22))
502+
>a : Symbol(a, Decl(objectSpread.ts, 131, 33))
503+
504+
f({ a: 1 }, { a: 'mismatch' })
505+
>f : Symbol(f, Decl(objectSpread.ts, 119, 42))
506+
>a : Symbol(a, Decl(objectSpread.ts, 132, 7))
507+
>a : Symbol(a, Decl(objectSpread.ts, 132, 17))
508+
509+
let overwriteId: { id: string, a: number, c: number, d: string } =
510+
>overwriteId : Symbol(overwriteId, Decl(objectSpread.ts, 133, 3))
511+
>id : Symbol(id, Decl(objectSpread.ts, 133, 18))
512+
>a : Symbol(a, Decl(objectSpread.ts, 133, 30))
513+
>c : Symbol(c, Decl(objectSpread.ts, 133, 41))
514+
>d : Symbol(d, Decl(objectSpread.ts, 133, 52))
515+
516+
f({ a: 1, id: true }, { c: 1, d: 'no' })
517+
>f : Symbol(f, Decl(objectSpread.ts, 119, 42))
518+
>a : Symbol(a, Decl(objectSpread.ts, 134, 7))
519+
>id : Symbol(id, Decl(objectSpread.ts, 134, 13))
520+
>c : Symbol(c, Decl(objectSpread.ts, 134, 27))
521+
>d : Symbol(d, Decl(objectSpread.ts, 134, 33))
522+
523+
function genericSpread<T, U>(t: T, u: U, v: T | U, w: T | { s: string }, obj: { x: number }) {
524+
>genericSpread : Symbol(genericSpread, Decl(objectSpread.ts, 134, 44))
525+
>T : Symbol(T, Decl(objectSpread.ts, 136, 23))
526+
>U : Symbol(U, Decl(objectSpread.ts, 136, 25))
527+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
528+
>T : Symbol(T, Decl(objectSpread.ts, 136, 23))
529+
>u : Symbol(u, Decl(objectSpread.ts, 136, 34))
530+
>U : Symbol(U, Decl(objectSpread.ts, 136, 25))
531+
>v : Symbol(v, Decl(objectSpread.ts, 136, 40))
532+
>T : Symbol(T, Decl(objectSpread.ts, 136, 23))
533+
>U : Symbol(U, Decl(objectSpread.ts, 136, 25))
534+
>w : Symbol(w, Decl(objectSpread.ts, 136, 50))
535+
>T : Symbol(T, Decl(objectSpread.ts, 136, 23))
536+
>s : Symbol(s, Decl(objectSpread.ts, 136, 59))
537+
>obj : Symbol(obj, Decl(objectSpread.ts, 136, 72))
538+
>x : Symbol(x, Decl(objectSpread.ts, 136, 79))
539+
540+
let x01 = { ...t };
541+
>x01 : Symbol(x01, Decl(objectSpread.ts, 137, 7))
542+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
543+
544+
let x02 = { ...t, ...t };
545+
>x02 : Symbol(x02, Decl(objectSpread.ts, 138, 7))
546+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
547+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
548+
549+
let x03 = { ...t, ...u };
550+
>x03 : Symbol(x03, Decl(objectSpread.ts, 139, 7))
551+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
552+
>u : Symbol(u, Decl(objectSpread.ts, 136, 34))
553+
554+
let x04 = { ...u, ...t };
555+
>x04 : Symbol(x04, Decl(objectSpread.ts, 140, 7))
556+
>u : Symbol(u, Decl(objectSpread.ts, 136, 34))
557+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
558+
559+
let x05 = { a: 5, b: 'hi', ...t };
560+
>x05 : Symbol(x05, Decl(objectSpread.ts, 141, 7))
561+
>a : Symbol(a, Decl(objectSpread.ts, 141, 15))
562+
>b : Symbol(b, Decl(objectSpread.ts, 141, 21))
563+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
564+
565+
let x06 = { ...t, a: 5, b: 'hi' };
566+
>x06 : Symbol(x06, Decl(objectSpread.ts, 142, 7))
567+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
568+
>a : Symbol(a, Decl(objectSpread.ts, 142, 21))
569+
>b : Symbol(b, Decl(objectSpread.ts, 142, 27))
570+
571+
let x07 = { a: 5, b: 'hi', ...t, c: true, ...obj };
572+
>x07 : Symbol(x07, Decl(objectSpread.ts, 143, 7))
573+
>a : Symbol(a, Decl(objectSpread.ts, 143, 15))
574+
>b : Symbol(b, Decl(objectSpread.ts, 143, 21))
575+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
576+
>c : Symbol(c, Decl(objectSpread.ts, 143, 36))
577+
>obj : Symbol(obj, Decl(objectSpread.ts, 136, 72))
578+
579+
let x09 = { a: 5, ...t, b: 'hi', c: true, ...obj };
580+
>x09 : Symbol(x09, Decl(objectSpread.ts, 144, 7))
581+
>a : Symbol(a, Decl(objectSpread.ts, 144, 15))
582+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
583+
>b : Symbol(b, Decl(objectSpread.ts, 144, 27))
584+
>c : Symbol(c, Decl(objectSpread.ts, 144, 36))
585+
>obj : Symbol(obj, Decl(objectSpread.ts, 136, 72))
586+
587+
let x10 = { a: 5, ...t, b: 'hi', ...u, ...obj };
588+
>x10 : Symbol(x10, Decl(objectSpread.ts, 145, 7))
589+
>a : Symbol(a, Decl(objectSpread.ts, 145, 15))
590+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
591+
>b : Symbol(b, Decl(objectSpread.ts, 145, 27))
592+
>u : Symbol(u, Decl(objectSpread.ts, 136, 34))
593+
>obj : Symbol(obj, Decl(objectSpread.ts, 136, 72))
594+
595+
let x11 = { ...v };
596+
>x11 : Symbol(x11, Decl(objectSpread.ts, 146, 7))
597+
>v : Symbol(v, Decl(objectSpread.ts, 136, 40))
598+
599+
let x12 = { ...v, ...obj };
600+
>x12 : Symbol(x12, Decl(objectSpread.ts, 147, 7))
601+
>v : Symbol(v, Decl(objectSpread.ts, 136, 40))
602+
>obj : Symbol(obj, Decl(objectSpread.ts, 136, 72))
603+
604+
let x13 = { ...w };
605+
>x13 : Symbol(x13, Decl(objectSpread.ts, 148, 7))
606+
>w : Symbol(w, Decl(objectSpread.ts, 136, 50))
607+
608+
let x14 = { ...w, ...obj };
609+
>x14 : Symbol(x14, Decl(objectSpread.ts, 149, 7))
610+
>w : Symbol(w, Decl(objectSpread.ts, 136, 50))
611+
>obj : Symbol(obj, Decl(objectSpread.ts, 136, 72))
612+
613+
let x15 = { ...t, ...v };
614+
>x15 : Symbol(x15, Decl(objectSpread.ts, 150, 7))
615+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
616+
>v : Symbol(v, Decl(objectSpread.ts, 136, 40))
617+
618+
let x16 = { ...t, ...w };
619+
>x16 : Symbol(x16, Decl(objectSpread.ts, 151, 7))
620+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
621+
>w : Symbol(w, Decl(objectSpread.ts, 136, 50))
622+
623+
let x17 = { ...t, ...w, ...obj };
624+
>x17 : Symbol(x17, Decl(objectSpread.ts, 152, 7))
625+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
626+
>w : Symbol(w, Decl(objectSpread.ts, 136, 50))
627+
>obj : Symbol(obj, Decl(objectSpread.ts, 136, 72))
628+
629+
let x18 = { ...t, ...v, ...w };
630+
>x18 : Symbol(x18, Decl(objectSpread.ts, 153, 7))
631+
>t : Symbol(t, Decl(objectSpread.ts, 136, 29))
632+
>v : Symbol(v, Decl(objectSpread.ts, 136, 40))
633+
>w : Symbol(w, Decl(objectSpread.ts, 136, 50))
634+
}
635+

0 commit comments

Comments
 (0)