Skip to content

Commit f2e30f6

Browse files
committed
Allow union and intersection as targets for object spread and rest, and distribute spread&rest over unions
1 parent e313fef commit f2e30f6

19 files changed

+911
-5
lines changed

src/compiler/checker.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3054,7 +3054,10 @@ 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+
if (source.flags & TypeFlags.Union) {
3058+
return getUnionType(map((<UnionType>source).types, t => getRestType(t, properties, symbol)));
3059+
}
3060+
30583061
const members = createMap<Symbol>();
30593062
const names = createMap<true>();
30603063
for (const name of properties) {
@@ -3095,7 +3098,7 @@ namespace ts {
30953098
let type: Type;
30963099
if (pattern.kind === SyntaxKind.ObjectBindingPattern) {
30973100
if (declaration.dotDotDotToken) {
3098-
if (!(parentType.flags & TypeFlags.Object)) {
3101+
if (isInvalidValidSpreadType(parentType)) {
30993102
error(declaration, Diagnostics.Rest_types_may_only_be_created_from_object_types);
31003103
return unknownType;
31013104
}
@@ -6102,11 +6105,19 @@ namespace ts {
61026105
* this function should be called in a left folding style, with left = previous result of getSpreadType
61036106
* and right = the new element to be spread.
61046107
*/
6105-
function getSpreadType(left: Type, right: Type, isFromObjectLiteral: boolean): ResolvedType | IntrinsicType {
6106-
Debug.assert(!!(left.flags & (TypeFlags.Object | TypeFlags.Any)) && !!(right.flags & (TypeFlags.Object | TypeFlags.Any)), "Only object types may be spread.");
6108+
function getSpreadType(left: Type, right: Type, isFromObjectLiteral: boolean): Type {
61076109
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) {
61086110
return anyType;
61096111
}
6112+
6113+
if (left.flags & TypeFlags.Union) {
6114+
return getUnionType(map((<UnionType>left).types, t => getSpreadType(t, right, isFromObjectLiteral)));
6115+
}
6116+
6117+
if (right.flags & TypeFlags.Union) {
6118+
return getUnionType(map((<UnionType>right).types, t => getSpreadType(left, t, isFromObjectLiteral)));
6119+
}
6120+
61106121
const members = createMap<Symbol>();
61116122
const skippedPrivateMembers = createMap<boolean>();
61126123
let stringIndexInfo: IndexInfo;
@@ -11438,7 +11449,7 @@ namespace ts {
1143811449
typeFlags = 0;
1143911450
}
1144011451
const type = checkExpression((memberDecl as SpreadAssignment).expression);
11441-
if (!(type.flags & (TypeFlags.Object | TypeFlags.Any))) {
11452+
if (!(type.flags & TypeFlags.Any) && isInvalidValidSpreadType(type)) {
1144211453
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
1144311454
return unknownType;
1144411455
}
@@ -11516,6 +11527,16 @@ namespace ts {
1151611527
}
1151711528
}
1151811529

11530+
function isInvalidValidSpreadType(type: Type): boolean {
11531+
if (type.flags & TypeFlags.Object) {
11532+
return isGenericMappedType(type);
11533+
}
11534+
else if (type.flags & TypeFlags.UnionOrIntersection) {
11535+
return forEach((<UnionOrIntersectionType>type).types, isInvalidValidSpreadType);
11536+
}
11537+
return true;
11538+
}
11539+
1151911540
function checkJsxSelfClosingElement(node: JsxSelfClosingElement) {
1152011541
checkJsxOpeningLikeElement(node);
1152111542
return jsxElementType || anyType;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//// [restIntersectionOrIntersection.ts]
2+
var intersection: { x: number, y: number } & { w: string, z: string };
3+
var union: { a: number, c: boolean } | { a: string, b: string };
4+
5+
6+
var rest1: { y: number, w: string, z: string };
7+
var {x, ...rest1 } = intersection;
8+
9+
var rest2: { c: boolean } | { b: string };
10+
var {a, ...rest2 } = union;
11+
12+
13+
14+
//// [restIntersectionOrIntersection.js]
15+
var __rest = (this && this.__rest) || function (s, e) {
16+
var t = {};
17+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
18+
t[p] = s[p];
19+
if (typeof Object.getOwnPropertySymbols === "function")
20+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
21+
t[p[i]] = s[p[i]];
22+
return t;
23+
};
24+
var intersection;
25+
var union;
26+
var rest1;
27+
var x = intersection.x, rest1 = __rest(intersection, ["x"]);
28+
var rest2;
29+
var a = union.a, rest2 = __rest(union, ["a"]);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=== tests/cases/compiler/restIntersectionOrIntersection.ts ===
2+
var intersection: { x: number, y: number } & { w: string, z: string };
3+
>intersection : Symbol(intersection, Decl(restIntersectionOrIntersection.ts, 0, 3))
4+
>x : Symbol(x, Decl(restIntersectionOrIntersection.ts, 0, 19))
5+
>y : Symbol(y, Decl(restIntersectionOrIntersection.ts, 0, 30))
6+
>w : Symbol(w, Decl(restIntersectionOrIntersection.ts, 0, 46))
7+
>z : Symbol(z, Decl(restIntersectionOrIntersection.ts, 0, 57))
8+
9+
var union: { a: number, c: boolean } | { a: string, b: string };
10+
>union : Symbol(union, Decl(restIntersectionOrIntersection.ts, 1, 3))
11+
>a : Symbol(a, Decl(restIntersectionOrIntersection.ts, 1, 12))
12+
>c : Symbol(c, Decl(restIntersectionOrIntersection.ts, 1, 23))
13+
>a : Symbol(a, Decl(restIntersectionOrIntersection.ts, 1, 40))
14+
>b : Symbol(b, Decl(restIntersectionOrIntersection.ts, 1, 51))
15+
16+
17+
var rest1: { y: number, w: string, z: string };
18+
>rest1 : Symbol(rest1, Decl(restIntersectionOrIntersection.ts, 4, 3), Decl(restIntersectionOrIntersection.ts, 5, 7))
19+
>y : Symbol(y, Decl(restIntersectionOrIntersection.ts, 4, 12))
20+
>w : Symbol(w, Decl(restIntersectionOrIntersection.ts, 4, 23))
21+
>z : Symbol(z, Decl(restIntersectionOrIntersection.ts, 4, 34))
22+
23+
var {x, ...rest1 } = intersection;
24+
>x : Symbol(x, Decl(restIntersectionOrIntersection.ts, 5, 5))
25+
>rest1 : Symbol(rest1, Decl(restIntersectionOrIntersection.ts, 4, 3), Decl(restIntersectionOrIntersection.ts, 5, 7))
26+
>intersection : Symbol(intersection, Decl(restIntersectionOrIntersection.ts, 0, 3))
27+
28+
var rest2: { c: boolean } | { b: string };
29+
>rest2 : Symbol(rest2, Decl(restIntersectionOrIntersection.ts, 7, 3), Decl(restIntersectionOrIntersection.ts, 8, 7))
30+
>c : Symbol(c, Decl(restIntersectionOrIntersection.ts, 7, 12))
31+
>b : Symbol(b, Decl(restIntersectionOrIntersection.ts, 7, 29))
32+
33+
var {a, ...rest2 } = union;
34+
>a : Symbol(a, Decl(restIntersectionOrIntersection.ts, 8, 5))
35+
>rest2 : Symbol(rest2, Decl(restIntersectionOrIntersection.ts, 7, 3), Decl(restIntersectionOrIntersection.ts, 8, 7))
36+
>union : Symbol(union, Decl(restIntersectionOrIntersection.ts, 1, 3))
37+
38+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=== tests/cases/compiler/restIntersectionOrIntersection.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 union: { a: number, c: boolean } | { a: string, b: string };
10+
>union : { a: number; c: boolean; } | { a: string; b: string; }
11+
>a : number
12+
>c : boolean
13+
>a : string
14+
>b : string
15+
16+
17+
var rest1: { y: number, w: string, z: string };
18+
>rest1 : { y: number; w: string; z: string; }
19+
>y : number
20+
>w : string
21+
>z : string
22+
23+
var {x, ...rest1 } = intersection;
24+
>x : number
25+
>rest1 : { y: number; w: string; z: string; }
26+
>intersection : { x: number; y: number; } & { w: string; z: string; }
27+
28+
var rest2: { c: boolean } | { b: string };
29+
>rest2 : { c: boolean; } | { b: string; }
30+
>c : boolean
31+
>b : string
32+
33+
var {a, ...rest2 } = union;
34+
>a : string | number
35+
>rest2 : { c: boolean; } | { b: string; }
36+
>union : { a: number; c: boolean; } | { a: string; b: string; }
37+
38+
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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(50,13): error TS2700: Rest types may only be created from object types.
13+
tests/cases/compiler/restInvalidArgumentType.ts(51,13): error TS2700: Rest types may only be created from object types.
14+
tests/cases/compiler/restInvalidArgumentType.ts(55,13): error TS2700: Rest types may only be created from object types.
15+
tests/cases/compiler/restInvalidArgumentType.ts(56,13): error TS2700: Rest types may only be created from object types.
16+
tests/cases/compiler/restInvalidArgumentType.ts(58,13): error TS2700: Rest types may only be created from object types.
17+
18+
19+
==== tests/cases/compiler/restInvalidArgumentType.ts (16 errors) ====
20+
enum E { v1, v2 };
21+
22+
function f<T extends { b: string }>(p1: T, p2: T[]) {
23+
var t: T;
24+
25+
var i: T["b"];
26+
var k: keyof T;
27+
28+
var mapped_generic: {[P in keyof T]: T[P]};
29+
var mapped: {[P in "b"]: T[P]};
30+
31+
var union_generic: T | { a: number };
32+
var union_primitive: { a: number } | number;
33+
34+
var intersection_generic: T & { a: number };
35+
var intersection_premitive: { a: number } | string;
36+
37+
var num: number;
38+
var str: number;
39+
40+
var u: undefined;
41+
var n: null;
42+
43+
var a: any;
44+
45+
var literal_string: "string";
46+
var literal_number: 42;
47+
48+
var e: E;
49+
50+
var {...r1} = p1; // Error, generic type paramterre
51+
~~
52+
!!! error TS2700: Rest types may only be created from object types.
53+
var {...r2} = p2; // OK
54+
var {...r3} = t; // Error, generic type paramter
55+
~~
56+
!!! error TS2700: Rest types may only be created from object types.
57+
58+
var {...r4} = i; // Error, index access
59+
~~
60+
!!! error TS2700: Rest types may only be created from object types.
61+
var {...r5} = k; // Error, index
62+
~~
63+
!!! error TS2700: Rest types may only be created from object types.
64+
65+
var {...r6} = mapped_generic; // Error, generic mapped object type
66+
~~
67+
!!! error TS2700: Rest types may only be created from object types.
68+
var {...r7} = mapped; // OK, non-generic mapped type
69+
70+
var {...r8} = union_generic; // Error, union with generic type parameter
71+
~~
72+
!!! error TS2700: Rest types may only be created from object types.
73+
var {...r9} = union_primitive; // Error, union with generic type parameter
74+
~~
75+
!!! error TS2700: Rest types may only be created from object types.
76+
77+
var {...r10} = intersection_generic; // Error, intersection with generic type parameter
78+
~~~
79+
!!! error TS2700: Rest types may only be created from object types.
80+
var {...r11} = intersection_premitive; // Error, intersection with generic type parameter
81+
~~~
82+
!!! error TS2700: Rest types may only be created from object types.
83+
84+
var {...r12} = num; // Error
85+
~~~
86+
!!! error TS2700: Rest types may only be created from object types.
87+
var {...r13} = str; // Error
88+
~~~
89+
!!! error TS2700: Rest types may only be created from object types.
90+
91+
var {...r14} = u; // Error
92+
~~~
93+
!!! error TS2700: Rest types may only be created from object types.
94+
var {...r15} = n; // Error
95+
~~~
96+
!!! error TS2700: Rest types may only be created from object types.
97+
98+
var {...r16} = a; // OK
99+
100+
var {...r17} = literal_string; // Error
101+
~~~
102+
!!! error TS2700: Rest types may only be created from object types.
103+
var {...r18} = literal_number; // Error
104+
~~~
105+
!!! error TS2700: Rest types may only be created from object types.
106+
107+
var {...r19} = e; // Error, enum
108+
~~~
109+
!!! error TS2700: Rest types may only be created from object types.
110+
}

0 commit comments

Comments
 (0)