Skip to content

Commit 4c9bdb9

Browse files
authored
Merge pull request #12552 from Microsoft/spreadRestIntersectionAndUnions
Spread & rest over intersection and unions
2 parents aaf0c89 + 505c153 commit 4c9bdb9

40 files changed

+1408
-39
lines changed

src/compiler/checker.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3054,7 +3054,15 @@ namespace ts {
30543054
}
30553055

30563056
function getRestType(source: Type, properties: PropertyName[], symbol: Symbol): Type {
3057-
Debug.assert(!!(source.flags & TypeFlags.Object), "Rest types only support object types right now.");
3057+
source = filterType(source, t => !(t.flags & TypeFlags.Nullable));
3058+
if (source.flags & TypeFlags.Never) {
3059+
return emptyObjectType;
3060+
}
3061+
3062+
if (source.flags & TypeFlags.Union) {
3063+
return mapType(source, t => getRestType(t, properties, symbol));
3064+
}
3065+
30583066
const members = createMap<Symbol>();
30593067
const names = createMap<true>();
30603068
for (const name of properties) {
@@ -3095,7 +3103,7 @@ namespace ts {
30953103
let type: Type;
30963104
if (pattern.kind === SyntaxKind.ObjectBindingPattern) {
30973105
if (declaration.dotDotDotToken) {
3098-
if (!(parentType.flags & TypeFlags.Object)) {
3106+
if (!isValidSpreadType(parentType)) {
30993107
error(declaration, Diagnostics.Rest_types_may_only_be_created_from_object_types);
31003108
return unknownType;
31013109
}
@@ -6110,11 +6118,25 @@ namespace ts {
61106118
* this function should be called in a left folding style, with left = previous result of getSpreadType
61116119
* and right = the new element to be spread.
61126120
*/
6113-
function getSpreadType(left: Type, right: Type, isFromObjectLiteral: boolean): ResolvedType | IntrinsicType {
6114-
Debug.assert(!!(left.flags & (TypeFlags.Object | TypeFlags.Any)) && !!(right.flags & (TypeFlags.Object | TypeFlags.Any)), "Only object types may be spread.");
6121+
function getSpreadType(left: Type, right: Type, isFromObjectLiteral: boolean): Type {
61156122
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) {
61166123
return anyType;
61176124
}
6125+
left = filterType(left, t => !(t.flags & TypeFlags.Nullable));
6126+
if (left.flags & TypeFlags.Never) {
6127+
return right;
6128+
}
6129+
right = filterType(right, t => !(t.flags & TypeFlags.Nullable));
6130+
if (right.flags & TypeFlags.Never) {
6131+
return left;
6132+
}
6133+
if (left.flags & TypeFlags.Union) {
6134+
return mapType(left, t => getSpreadType(t, right, isFromObjectLiteral));
6135+
}
6136+
if (right.flags & TypeFlags.Union) {
6137+
return mapType(right, t => getSpreadType(left, t, isFromObjectLiteral));
6138+
}
6139+
61186140
const members = createMap<Symbol>();
61196141
const skippedPrivateMembers = createMap<boolean>();
61206142
let stringIndexInfo: IndexInfo;
@@ -11446,7 +11468,7 @@ namespace ts {
1144611468
typeFlags = 0;
1144711469
}
1144811470
const type = checkExpression((memberDecl as SpreadAssignment).expression);
11449-
if (!(type.flags & (TypeFlags.Object | TypeFlags.Any))) {
11471+
if (!isValidSpreadType(type)) {
1145011472
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
1145111473
return unknownType;
1145211474
}
@@ -11524,6 +11546,12 @@ namespace ts {
1152411546
}
1152511547
}
1152611548

11549+
function isValidSpreadType(type: Type): boolean {
11550+
return !!(type.flags & (TypeFlags.Any | TypeFlags.Null | TypeFlags.Undefined) ||
11551+
type.flags & TypeFlags.Object && !isGenericMappedType(type) ||
11552+
type.flags & TypeFlags.UnionOrIntersection && !forEach((<UnionOrIntersectionType>type).types, t => !isValidSpreadType(t)));
11553+
}
11554+
1152711555
function checkJsxSelfClosingElement(node: JsxSelfClosingElement) {
1152811556
checkJsxOpeningLikeElement(node);
1152911557
return jsxElementType || anyType;

src/compiler/transformers/destructuring.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ namespace ts {
459459
var t = {};
460460
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
461461
t[p] = s[p];
462-
if (typeof Object.getOwnPropertySymbols === "function")
462+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
463463
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
464464
t[p[i]] = s[p[i]];
465465
return t;

tests/baselines/reference/objectRest.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ var __rest = (this && this.__rest) || function (s, e) {
4343
var t = {};
4444
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4545
t[p] = s[p];
46-
if (typeof Object.getOwnPropertySymbols === "function")
46+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
4747
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
4848
t[p[i]] = s[p[i]];
4949
return t;

tests/baselines/reference/objectRestAssignment.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ var __rest = (this && this.__rest) || function (s, e) {
1919
var t = {};
2020
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
2121
t[p] = s[p];
22-
if (typeof Object.getOwnPropertySymbols === "function")
22+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
2323
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
2424
t[p[i]] = s[p[i]];
2525
return t;

tests/baselines/reference/objectRestForOf.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var __rest = (this && this.__rest) || function (s, e) {
2727
var t = {};
2828
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
2929
t[p] = s[p];
30-
if (typeof Object.getOwnPropertySymbols === "function")
30+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
3131
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
3232
t[p[i]] = s[p[i]];
3333
return t;

tests/baselines/reference/objectRestNegative.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ var __rest = (this && this.__rest) || function (s, e) {
2323
var t = {};
2424
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
2525
t[p] = s[p];
26-
if (typeof Object.getOwnPropertySymbols === "function")
26+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
2727
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
2828
t[p[i]] = s[p[i]];
2929
return t;

tests/baselines/reference/objectRestParameter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var __rest = (this && this.__rest) || function (s, e) {
2222
var t = {};
2323
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
2424
t[p] = s[p];
25-
if (typeof Object.getOwnPropertySymbols === "function")
25+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
2626
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
2727
t[p[i]] = s[p[i]];
2828
return t;

tests/baselines/reference/objectSpreadNegative.errors.txt

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,18 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(25,1): error TS2322
77
Property 's' is missing in type '{ b: boolean; }'.
88
tests/cases/conformance/types/spread/objectSpreadNegative.ts(28,36): error TS2300: Duplicate identifier 'b'.
99
tests/cases/conformance/types/spread/objectSpreadNegative.ts(28,53): error TS2300: Duplicate identifier 'b'.
10-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(32,20): error TS2698: Spread types may only be created from object types.
11-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(33,24): error TS2698: Spread types may only be created from object types.
12-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(34,19): error TS2698: Spread types may only be created from object types.
13-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(35,19): error TS2698: Spread types may only be created from object types.
14-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(37,20): error TS2698: Spread types may only be created from object types.
15-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(39,19): error TS2698: Spread types may only be created from object types.
16-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(44,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{}' has no compatible call signatures.
17-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(48,12): error TS2339: Property 'b' does not exist on type '{}'.
18-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(54,9): error TS2339: Property 'm' does not exist on type '{ p: number; }'.
19-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(58,14): error TS2698: Spread types may only be created from object types.
20-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(61,14): error TS2698: Spread types may only be created from object types.
10+
tests/cases/conformance/types/spread/objectSpreadNegative.ts(32,19): error TS2698: Spread types may only be created from object types.
11+
tests/cases/conformance/types/spread/objectSpreadNegative.ts(33,19): error TS2698: Spread types may only be created from object types.
12+
tests/cases/conformance/types/spread/objectSpreadNegative.ts(35,20): error TS2698: Spread types may only be created from object types.
13+
tests/cases/conformance/types/spread/objectSpreadNegative.ts(37,19): error TS2698: Spread types may only be created from object types.
14+
tests/cases/conformance/types/spread/objectSpreadNegative.ts(42,1): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '{}' has no compatible call signatures.
15+
tests/cases/conformance/types/spread/objectSpreadNegative.ts(46,12): error TS2339: Property 'b' does not exist on type '{}'.
16+
tests/cases/conformance/types/spread/objectSpreadNegative.ts(52,9): error TS2339: Property 'm' does not exist on type '{ p: number; }'.
17+
tests/cases/conformance/types/spread/objectSpreadNegative.ts(56,14): error TS2698: Spread types may only be created from object types.
18+
tests/cases/conformance/types/spread/objectSpreadNegative.ts(59,14): error TS2698: Spread types may only be created from object types.
2119

2220

23-
==== tests/cases/conformance/types/spread/objectSpreadNegative.ts (17 errors) ====
21+
==== tests/cases/conformance/types/spread/objectSpreadNegative.ts (15 errors) ====
2422
let o = { a: 1, b: 'no' }
2523

2624
/// private propagates
@@ -66,13 +64,7 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(61,14): error TS269
6664
!!! error TS2300: Duplicate identifier 'b'.
6765
let duplicatedSpread = { ...o, ...o }
6866

69-
// null, undefined and primitives are not allowed
70-
let spreadNull = { ...null };
71-
~~~~~~~
72-
!!! error TS2698: Spread types may only be created from object types.
73-
let spreadUndefind = { ...undefined };
74-
~~~~~~~~~~~~
75-
!!! error TS2698: Spread types may only be created from object types.
67+
// primitives are not allowed
7668
let spreadNum = { ...12 };
7769
~~~~~
7870
!!! error TS2698: Spread types may only be created from object types.

tests/baselines/reference/objectSpreadNegative.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,7 @@ spread = b; // error, missing 's'
2929
let duplicated = { b: 'bad', ...o, b: 'bad', ...o2, b: 'bad' }
3030
let duplicatedSpread = { ...o, ...o }
3131

32-
// null, undefined and primitives are not allowed
33-
let spreadNull = { ...null };
34-
let spreadUndefind = { ...undefined };
32+
// primitives are not allowed
3533
let spreadNum = { ...12 };
3634
let spreadSum = { ...1 + 1 };
3735
spreadSum.toFixed(); // error, no methods from number
@@ -108,9 +106,7 @@ spread = b; // error, missing 's'
108106
// literal repeats are not allowed, but spread repeats are fine
109107
var duplicated = __assign({ b: 'bad' }, o, { b: 'bad' }, o2, { b: 'bad' });
110108
var duplicatedSpread = __assign({}, o, o);
111-
// null, undefined and primitives are not allowed
112-
var spreadNull = __assign({}, null);
113-
var spreadUndefind = __assign({}, undefined);
109+
// primitives are not allowed
114110
var spreadNum = __assign({}, 12);
115111
var spreadSum = __assign({}, 1 + 1);
116112
spreadSum.toFixed(); // error, no methods from number
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//// [restIntersection.ts]
2+
var intersection: { x: number, y: number } & { w: string, z: string };
3+
4+
var rest1: { y: number, w: string, z: string };
5+
var {x, ...rest1 } = intersection;
6+
7+
8+
//// [restIntersection.js]
9+
var __rest = (this && this.__rest) || function (s, e) {
10+
var t = {};
11+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
12+
t[p] = s[p];
13+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
14+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
15+
t[p[i]] = s[p[i]];
16+
return t;
17+
};
18+
var intersection;
19+
var rest1;
20+
var x = intersection.x, rest1 = __rest(intersection, ["x"]);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/compiler/restIntersection.ts ===
2+
var intersection: { x: number, y: number } & { w: string, z: string };
3+
>intersection : Symbol(intersection, Decl(restIntersection.ts, 0, 3))
4+
>x : Symbol(x, Decl(restIntersection.ts, 0, 19))
5+
>y : Symbol(y, Decl(restIntersection.ts, 0, 30))
6+
>w : Symbol(w, Decl(restIntersection.ts, 0, 46))
7+
>z : Symbol(z, Decl(restIntersection.ts, 0, 57))
8+
9+
var rest1: { y: number, w: string, z: string };
10+
>rest1 : Symbol(rest1, Decl(restIntersection.ts, 2, 3), Decl(restIntersection.ts, 3, 7))
11+
>y : Symbol(y, Decl(restIntersection.ts, 2, 12))
12+
>w : Symbol(w, Decl(restIntersection.ts, 2, 23))
13+
>z : Symbol(z, Decl(restIntersection.ts, 2, 34))
14+
15+
var {x, ...rest1 } = intersection;
16+
>x : Symbol(x, Decl(restIntersection.ts, 3, 5))
17+
>rest1 : Symbol(rest1, Decl(restIntersection.ts, 2, 3), Decl(restIntersection.ts, 3, 7))
18+
>intersection : Symbol(intersection, Decl(restIntersection.ts, 0, 3))
19+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/compiler/restIntersection.ts ===
2+
var intersection: { x: number, y: number } & { w: string, z: string };
3+
>intersection : { x: number; y: number; } & { w: string; z: string; }
4+
>x : number
5+
>y : number
6+
>w : string
7+
>z : string
8+
9+
var rest1: { y: number, w: string, z: string };
10+
>rest1 : { y: number; w: string; z: string; }
11+
>y : number
12+
>w : string
13+
>z : string
14+
15+
var {x, ...rest1 } = intersection;
16+
>x : number
17+
>rest1 : { y: number; w: string; z: string; }
18+
>intersection : { x: number; y: number; } & { w: string; z: string; }
19+
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
tests/cases/compiler/restInvalidArgumentType.ts(31,13): error TS2700: Rest types may only be created from object types.
2+
tests/cases/compiler/restInvalidArgumentType.ts(33,13): error TS2700: Rest types may only be created from object types.
3+
tests/cases/compiler/restInvalidArgumentType.ts(35,13): error TS2700: Rest types may only be created from object types.
4+
tests/cases/compiler/restInvalidArgumentType.ts(36,13): error TS2700: Rest types may only be created from object types.
5+
tests/cases/compiler/restInvalidArgumentType.ts(38,13): error TS2700: Rest types may only be created from object types.
6+
tests/cases/compiler/restInvalidArgumentType.ts(41,13): error TS2700: Rest types may only be created from object types.
7+
tests/cases/compiler/restInvalidArgumentType.ts(42,13): error TS2700: Rest types may only be created from object types.
8+
tests/cases/compiler/restInvalidArgumentType.ts(44,13): error TS2700: Rest types may only be created from object types.
9+
tests/cases/compiler/restInvalidArgumentType.ts(45,13): error TS2700: Rest types may only be created from object types.
10+
tests/cases/compiler/restInvalidArgumentType.ts(47,13): error TS2700: Rest types may only be created from object types.
11+
tests/cases/compiler/restInvalidArgumentType.ts(48,13): error TS2700: Rest types may only be created from object types.
12+
tests/cases/compiler/restInvalidArgumentType.ts(55,13): error TS2700: Rest types may only be created from object types.
13+
tests/cases/compiler/restInvalidArgumentType.ts(56,13): error TS2700: Rest types may only be created from object types.
14+
tests/cases/compiler/restInvalidArgumentType.ts(58,13): error TS2700: Rest types may only be created from object types.
15+
16+
17+
==== tests/cases/compiler/restInvalidArgumentType.ts (14 errors) ====
18+
enum E { v1, v2 };
19+
20+
function f<T extends { b: string }>(p1: T, p2: T[]) {
21+
var t: T;
22+
23+
var i: T["b"];
24+
var k: keyof T;
25+
26+
var mapped_generic: {[P in keyof T]: T[P]};
27+
var mapped: {[P in "b"]: T[P]};
28+
29+
var union_generic: T | { a: number };
30+
var union_primitive: { a: number } | number;
31+
32+
var intersection_generic: T & { a: number };
33+
var intersection_premitive: { a: number } | string;
34+
35+
var num: number;
36+
var str: number;
37+
38+
var u: undefined;
39+
var n: null;
40+
41+
var a: any;
42+
43+
var literal_string: "string";
44+
var literal_number: 42;
45+
46+
var e: E;
47+
48+
var {...r1} = p1; // Error, generic type paramterre
49+
~~
50+
!!! error TS2700: Rest types may only be created from object types.
51+
var {...r2} = p2; // OK
52+
var {...r3} = t; // Error, generic type paramter
53+
~~
54+
!!! error TS2700: Rest types may only be created from object types.
55+
56+
var {...r4} = i; // Error, index access
57+
~~
58+
!!! error TS2700: Rest types may only be created from object types.
59+
var {...r5} = k; // Error, index
60+
~~
61+
!!! error TS2700: Rest types may only be created from object types.
62+
63+
var {...r6} = mapped_generic; // Error, generic mapped object type
64+
~~
65+
!!! error TS2700: Rest types may only be created from object types.
66+
var {...r7} = mapped; // OK, non-generic mapped type
67+
68+
var {...r8} = union_generic; // Error, union with generic type parameter
69+
~~
70+
!!! error TS2700: Rest types may only be created from object types.
71+
var {...r9} = union_primitive; // Error, union with generic type parameter
72+
~~
73+
!!! error TS2700: Rest types may only be created from object types.
74+
75+
var {...r10} = intersection_generic; // Error, intersection with generic type parameter
76+
~~~
77+
!!! error TS2700: Rest types may only be created from object types.
78+
var {...r11} = intersection_premitive; // Error, intersection with generic type parameter
79+
~~~
80+
!!! error TS2700: Rest types may only be created from object types.
81+
82+
var {...r12} = num; // Error
83+
~~~
84+
!!! error TS2700: Rest types may only be created from object types.
85+
var {...r13} = str; // Error
86+
~~~
87+
!!! error TS2700: Rest types may only be created from object types.
88+
89+
var {...r14} = u; // OK
90+
var {...r15} = n; // OK
91+
92+
var {...r16} = a; // OK
93+
94+
var {...r17} = literal_string; // Error
95+
~~~
96+
!!! error TS2700: Rest types may only be created from object types.
97+
var {...r18} = literal_number; // Error
98+
~~~
99+
!!! error TS2700: Rest types may only be created from object types.
100+
101+
var {...r19} = e; // Error, enum
102+
~~~
103+
!!! error TS2700: Rest types may only be created from object types.
104+
}

0 commit comments

Comments
 (0)