Skip to content

Commit 216f286

Browse files
committed
Handel null and undefined in object spread and rest
1 parent 5c4f145 commit 216f286

20 files changed

+580
-41
lines changed

src/compiler/checker.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3055,7 +3055,10 @@ namespace ts {
30553055

30563056
function getRestType(source: Type, properties: PropertyName[], symbol: Symbol): Type {
30573057
if (source.flags & TypeFlags.Union) {
3058-
return getUnionType(map((<UnionType>source).types, t => getRestType(t, properties, symbol)));
3058+
const types = filter((<UnionType>source).types, t => !(t.flags & TypeFlags.Nullable));
3059+
if (types.length) {
3060+
return getUnionType(map(types, t => getRestType(t, properties, symbol)));
3061+
}
30593062
}
30603063

30613064
const members = createMap<Symbol>();
@@ -6111,11 +6114,29 @@ namespace ts {
61116114
}
61126115

61136116
if (left.flags & TypeFlags.Union) {
6114-
return getUnionType(map((<UnionType>left).types, t => getSpreadType(t, right, isFromObjectLiteral)));
6117+
const types = filter((<UnionType>left).types, t => !(t.flags & TypeFlags.Nullable));
6118+
if (types.length) {
6119+
return getUnionType(map(types, t => getSpreadType(t, right, isFromObjectLiteral)));
6120+
}
6121+
else {
6122+
left = emptyObjectType
6123+
}
6124+
}
6125+
else if (left.flags & TypeFlags.Nullable) {
6126+
left = emptyObjectType;
61156127
}
61166128

61176129
if (right.flags & TypeFlags.Union) {
6118-
return getUnionType(map((<UnionType>right).types, t => getSpreadType(left, t, isFromObjectLiteral)));
6130+
const types = filter((<UnionType>right).types, t => !(t.flags & TypeFlags.Nullable));
6131+
if (types.length) {
6132+
return getUnionType(map(types, t => getSpreadType(left, t, isFromObjectLiteral)));
6133+
}
6134+
else {
6135+
right = emptyObjectType
6136+
}
6137+
}
6138+
else if (right.flags & TypeFlags.Nullable) {
6139+
right = emptyObjectType;
61196140
}
61206141

61216142
const members = createMap<Symbol>();
@@ -11528,7 +11549,7 @@ namespace ts {
1152811549
}
1152911550

1153011551
function isValidSpreadType(type: Type): boolean {
11531-
if (type.flags & TypeFlags.Any) {
11552+
if (type.flags & (TypeFlags.Any | TypeFlags.Null | TypeFlags.Undefined)) {
1153211553
return true;
1153311554
}
1153411555
if (type.flags & TypeFlags.Object) {
@@ -11540,6 +11561,7 @@ namespace ts {
1154011561
return false;
1154111562
}
1154211563
}
11564+
return true;
1154311565
}
1154411566
return false;
1154511567
}

tests/baselines/reference/objectSpreadNegative.errors.txt

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ 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.
1210
tests/cases/conformance/types/spread/objectSpreadNegative.ts(34,19): error TS2698: Spread types may only be created from object types.
1311
tests/cases/conformance/types/spread/objectSpreadNegative.ts(35,19): error TS2698: Spread types may only be created from object types.
1412
tests/cases/conformance/types/spread/objectSpreadNegative.ts(37,20): error TS2698: Spread types may only be created from object types.
@@ -20,7 +18,7 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(58,14): error TS269
2018
tests/cases/conformance/types/spread/objectSpreadNegative.ts(61,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
@@ -68,11 +66,7 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(61,14): error TS269
6866

6967
// null, undefined and primitives are not allowed
7068
let spreadNull = { ...null };
71-
~~~~~~~
72-
!!! error TS2698: Spread types may only be created from object types.
7369
let spreadUndefind = { ...undefined };
74-
~~~~~~~~~~~~
75-
!!! error TS2698: Spread types may only be created from object types.
7670
let spreadNum = { ...12 };
7771
~~~~~
7872
!!! error TS2698: Spread types may only be created from object types.

tests/baselines/reference/restInvalidArgumentType.errors.txt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ tests/cases/compiler/restInvalidArgumentType.ts(44,13): error TS2700: Rest types
99
tests/cases/compiler/restInvalidArgumentType.ts(45,13): error TS2700: Rest types may only be created from object types.
1010
tests/cases/compiler/restInvalidArgumentType.ts(47,13): error TS2700: Rest types may only be created from object types.
1111
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.
1412
tests/cases/compiler/restInvalidArgumentType.ts(55,13): error TS2700: Rest types may only be created from object types.
1513
tests/cases/compiler/restInvalidArgumentType.ts(56,13): error TS2700: Rest types may only be created from object types.
1614
tests/cases/compiler/restInvalidArgumentType.ts(58,13): error TS2700: Rest types may only be created from object types.
1715

1816

19-
==== tests/cases/compiler/restInvalidArgumentType.ts (16 errors) ====
17+
==== tests/cases/compiler/restInvalidArgumentType.ts (14 errors) ====
2018
enum E { v1, v2 };
2119

2220
function f<T extends { b: string }>(p1: T, p2: T[]) {
@@ -88,12 +86,8 @@ tests/cases/compiler/restInvalidArgumentType.ts(58,13): error TS2700: Rest types
8886
~~~
8987
!!! error TS2700: Rest types may only be created from object types.
9088

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.
89+
var {...r14} = u; // OK
90+
var {...r15} = n; // OK
9791

9892
var {...r16} = a; // OK
9993

tests/baselines/reference/restInvalidArgumentType.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ function f<T extends { b: string }>(p1: T, p2: T[]) {
4848
var {...r12} = num; // Error
4949
var {...r13} = str; // Error
5050

51-
var {...r14} = u; // Error
52-
var {...r15} = n; // Error
51+
var {...r14} = u; // OK
52+
var {...r15} = n; // OK
5353

5454
var {...r16} = a; // OK
5555

@@ -106,8 +106,8 @@ function f(p1, p2) {
106106
var r11 = __rest(intersection_premitive, []); // Error, intersection with generic type parameter
107107
var r12 = __rest(num, []); // Error
108108
var r13 = __rest(str, []); // Error
109-
var r14 = __rest(u, []); // Error
110-
var r15 = __rest(n, []); // Error
109+
var r14 = __rest(u, []); // OK
110+
var r15 = __rest(n, []); // OK
111111
var r16 = __rest(a, []); // OK
112112
var r17 = __rest(literal_string, []); // Error
113113
var r18 = __rest(literal_number, []); // Error
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//// [restUnion.ts]
2+
var union: { a: number, c: boolean } | { a: string, b: string };
3+
4+
var rest1: { c: boolean } | { b: string };
5+
var {a, ...rest1 } = union;
6+
7+
8+
var undefinedUnion: { n: number } | undefined;
9+
var rest2: {};
10+
var {n, ...rest2 } = undefinedUnion;
11+
12+
13+
var nullUnion: { n: number } | null;
14+
var rest3: {};
15+
var {n, ...rest3 } = nullUnion;
16+
17+
18+
//// [restUnion.js]
19+
var __rest = (this && this.__rest) || function (s, e) {
20+
var t = {};
21+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
22+
t[p] = s[p];
23+
if (typeof Object.getOwnPropertySymbols === "function")
24+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
25+
t[p[i]] = s[p[i]];
26+
return t;
27+
};
28+
var union;
29+
var rest1;
30+
var a = union.a, rest1 = __rest(union, ["a"]);
31+
var undefinedUnion;
32+
var rest2;
33+
var n = undefinedUnion.n, rest2 = __rest(undefinedUnion, ["n"]);
34+
var nullUnion;
35+
var rest3;
36+
var n = nullUnion.n, rest3 = __rest(nullUnion, ["n"]);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/compiler/restUnion.ts ===
2+
var union: { a: number, c: boolean } | { a: string, b: string };
3+
>union : Symbol(union, Decl(restUnion.ts, 0, 3))
4+
>a : Symbol(a, Decl(restUnion.ts, 0, 12))
5+
>c : Symbol(c, Decl(restUnion.ts, 0, 23))
6+
>a : Symbol(a, Decl(restUnion.ts, 0, 40))
7+
>b : Symbol(b, Decl(restUnion.ts, 0, 51))
8+
9+
var rest1: { c: boolean } | { b: string };
10+
>rest1 : Symbol(rest1, Decl(restUnion.ts, 2, 3), Decl(restUnion.ts, 3, 7))
11+
>c : Symbol(c, Decl(restUnion.ts, 2, 12))
12+
>b : Symbol(b, Decl(restUnion.ts, 2, 29))
13+
14+
var {a, ...rest1 } = union;
15+
>a : Symbol(a, Decl(restUnion.ts, 3, 5))
16+
>rest1 : Symbol(rest1, Decl(restUnion.ts, 2, 3), Decl(restUnion.ts, 3, 7))
17+
>union : Symbol(union, Decl(restUnion.ts, 0, 3))
18+
19+
20+
var undefinedUnion: { n: number } | undefined;
21+
>undefinedUnion : Symbol(undefinedUnion, Decl(restUnion.ts, 6, 3))
22+
>n : Symbol(n, Decl(restUnion.ts, 6, 21))
23+
24+
var rest2: {};
25+
>rest2 : Symbol(rest2, Decl(restUnion.ts, 7, 3), Decl(restUnion.ts, 8, 7))
26+
27+
var {n, ...rest2 } = undefinedUnion;
28+
>n : Symbol(n, Decl(restUnion.ts, 8, 5), Decl(restUnion.ts, 13, 5))
29+
>rest2 : Symbol(rest2, Decl(restUnion.ts, 7, 3), Decl(restUnion.ts, 8, 7))
30+
>undefinedUnion : Symbol(undefinedUnion, Decl(restUnion.ts, 6, 3))
31+
32+
33+
var nullUnion: { n: number } | null;
34+
>nullUnion : Symbol(nullUnion, Decl(restUnion.ts, 11, 3))
35+
>n : Symbol(n, Decl(restUnion.ts, 11, 16))
36+
37+
var rest3: {};
38+
>rest3 : Symbol(rest3, Decl(restUnion.ts, 12, 3), Decl(restUnion.ts, 13, 7))
39+
40+
var {n, ...rest3 } = nullUnion;
41+
>n : Symbol(n, Decl(restUnion.ts, 8, 5), Decl(restUnion.ts, 13, 5))
42+
>rest3 : Symbol(rest3, Decl(restUnion.ts, 12, 3), Decl(restUnion.ts, 13, 7))
43+
>nullUnion : Symbol(nullUnion, Decl(restUnion.ts, 11, 3))
44+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
=== tests/cases/compiler/restUnion.ts ===
2+
var union: { a: number, c: boolean } | { a: string, b: string };
3+
>union : { a: number; c: boolean; } | { a: string; b: string; }
4+
>a : number
5+
>c : boolean
6+
>a : string
7+
>b : string
8+
9+
var rest1: { c: boolean } | { b: string };
10+
>rest1 : { c: boolean; } | { b: string; }
11+
>c : boolean
12+
>b : string
13+
14+
var {a, ...rest1 } = union;
15+
>a : string | number
16+
>rest1 : { c: boolean; } | { b: string; }
17+
>union : { a: number; c: boolean; } | { a: string; b: string; }
18+
19+
20+
var undefinedUnion: { n: number } | undefined;
21+
>undefinedUnion : { n: number; }
22+
>n : number
23+
24+
var rest2: {};
25+
>rest2 : {}
26+
27+
var {n, ...rest2 } = undefinedUnion;
28+
>n : number
29+
>rest2 : {}
30+
>undefinedUnion : { n: number; }
31+
32+
33+
var nullUnion: { n: number } | null;
34+
>nullUnion : { n: number; }
35+
>n : number
36+
>null : null
37+
38+
var rest3: {};
39+
>rest3 : {}
40+
41+
var {n, ...rest3 } = nullUnion;
42+
>n : number
43+
>rest3 : {}
44+
>nullUnion : { n: number; }
45+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//// [restUnion2.ts]
2+
3+
declare const undefinedUnion: { n: number } | undefined;
4+
var rest2: { n: number };
5+
var {...rest2 } = undefinedUnion;
6+
7+
8+
declare const nullUnion: { n: number } | null;
9+
var rest3: { n: number };
10+
var {...rest3 } = nullUnion;
11+
12+
13+
declare const nullAndUndefinedUnion: null | undefined;
14+
var rest4: { };
15+
var {...rest4 } = nullAndUndefinedUnion;
16+
17+
declare const unionWithIntersection: ({ n: number } & { s: string }) & undefined | null;
18+
var rest5: { n: number, s: string };
19+
var {...rest5 } = unionWithIntersection;
20+
21+
//// [restUnion2.js]
22+
var __rest = (this && this.__rest) || function (s, e) {
23+
var t = {};
24+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
25+
t[p] = s[p];
26+
if (typeof Object.getOwnPropertySymbols === "function")
27+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
28+
t[p[i]] = s[p[i]];
29+
return t;
30+
};
31+
var rest2;
32+
var rest2 = __rest(undefinedUnion, []);
33+
var rest3;
34+
var rest3 = __rest(nullUnion, []);
35+
var rest4;
36+
var rest4 = __rest(nullAndUndefinedUnion, []);
37+
var rest5;
38+
var rest5 = __rest(unionWithIntersection, []);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
=== tests/cases/compiler/restUnion2.ts ===
2+
3+
declare const undefinedUnion: { n: number } | undefined;
4+
>undefinedUnion : Symbol(undefinedUnion, Decl(restUnion2.ts, 1, 13))
5+
>n : Symbol(n, Decl(restUnion2.ts, 1, 31))
6+
7+
var rest2: { n: number };
8+
>rest2 : Symbol(rest2, Decl(restUnion2.ts, 2, 3), Decl(restUnion2.ts, 3, 5))
9+
>n : Symbol(n, Decl(restUnion2.ts, 2, 12))
10+
11+
var {...rest2 } = undefinedUnion;
12+
>rest2 : Symbol(rest2, Decl(restUnion2.ts, 2, 3), Decl(restUnion2.ts, 3, 5))
13+
>undefinedUnion : Symbol(undefinedUnion, Decl(restUnion2.ts, 1, 13))
14+
15+
16+
declare const nullUnion: { n: number } | null;
17+
>nullUnion : Symbol(nullUnion, Decl(restUnion2.ts, 6, 13))
18+
>n : Symbol(n, Decl(restUnion2.ts, 6, 26))
19+
20+
var rest3: { n: number };
21+
>rest3 : Symbol(rest3, Decl(restUnion2.ts, 7, 3), Decl(restUnion2.ts, 8, 5))
22+
>n : Symbol(n, Decl(restUnion2.ts, 7, 12))
23+
24+
var {...rest3 } = nullUnion;
25+
>rest3 : Symbol(rest3, Decl(restUnion2.ts, 7, 3), Decl(restUnion2.ts, 8, 5))
26+
>nullUnion : Symbol(nullUnion, Decl(restUnion2.ts, 6, 13))
27+
28+
29+
declare const nullAndUndefinedUnion: null | undefined;
30+
>nullAndUndefinedUnion : Symbol(nullAndUndefinedUnion, Decl(restUnion2.ts, 11, 13))
31+
32+
var rest4: { };
33+
>rest4 : Symbol(rest4, Decl(restUnion2.ts, 12, 3), Decl(restUnion2.ts, 13, 5))
34+
35+
var {...rest4 } = nullAndUndefinedUnion;
36+
>rest4 : Symbol(rest4, Decl(restUnion2.ts, 12, 3), Decl(restUnion2.ts, 13, 5))
37+
>nullAndUndefinedUnion : Symbol(nullAndUndefinedUnion, Decl(restUnion2.ts, 11, 13))
38+
39+
declare const unionWithIntersection: ({ n: number } & { s: string }) & undefined | null;
40+
>unionWithIntersection : Symbol(unionWithIntersection, Decl(restUnion2.ts, 15, 13))
41+
>n : Symbol(n, Decl(restUnion2.ts, 15, 39))
42+
>s : Symbol(s, Decl(restUnion2.ts, 15, 55))
43+
44+
var rest5: { n: number, s: string };
45+
>rest5 : Symbol(rest5, Decl(restUnion2.ts, 16, 3), Decl(restUnion2.ts, 17, 5))
46+
>n : Symbol(n, Decl(restUnion2.ts, 16, 12))
47+
>s : Symbol(s, Decl(restUnion2.ts, 16, 23))
48+
49+
var {...rest5 } = unionWithIntersection;
50+
>rest5 : Symbol(rest5, Decl(restUnion2.ts, 16, 3), Decl(restUnion2.ts, 17, 5))
51+
>unionWithIntersection : Symbol(unionWithIntersection, Decl(restUnion2.ts, 15, 13))
52+

0 commit comments

Comments
 (0)