Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a6d44aa

Browse filesBrowse files
authored
Map stale empty object type in union into fresh empty object type after spread is complete (microsoft#34839)
* Map stale empty object type in union into fresh empty object type after spread is complete * Accept minor baseline diff
1 parent 0d993ac commit a6d44aa
Copy full SHA for a6d44aa

6 files changed

+214-4Lines changed: 214 additions & 4 deletions
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/checker.ts‎

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22090,8 +22090,13 @@ namespace ts {
2209022090
if (spread !== emptyObjectType) {
2209122091
if (propertiesArray.length > 0) {
2209222092
spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, objectFlags, inConstContext);
22093+
propertiesArray = [];
22094+
propertiesTable = createSymbolTable();
22095+
hasComputedStringProperty = false;
22096+
hasComputedNumberProperty = false;
2209322097
}
22094-
return spread;
22098+
// remap the raw emptyObjectType fed in at the top into a fresh empty object literal type, unique to this use site
22099+
return mapType(spread, t => t === emptyObjectType ? createObjectLiteralType() : t);
2209522100
}
2209622101

2209722102
return createObjectLiteralType();
Collapse file

‎tests/baselines/reference/objectSpread.types‎

Copy file name to clipboardExpand all lines: tests/baselines/reference/objectSpread.types
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ function conditionalSpreadBoolean(b: boolean) : { x: number, y: number } {
293293
}
294294
let o2 = { ...b && { x: 21 }}
295295
>o2 : {}
296-
>{ ...b && { x: 21 }} : {} | { x: number; }
296+
>{ ...b && { x: 21 }} : { x: number; } | {}
297297
>b && { x: 21 } : false | { x: number; }
298298
>b : boolean
299299
>{ x: 21 } : { x: number; }
@@ -334,7 +334,7 @@ function conditionalSpreadNumber(nt: number): { x: number, y: number } {
334334
}
335335
let o2 = { ...nt && { x: nt }}
336336
>o2 : {}
337-
>{ ...nt && { x: nt }} : {} | { x: number; }
337+
>{ ...nt && { x: nt }} : { x: number; } | {}
338338
>nt && { x: nt } : 0 | { x: number; }
339339
>nt : number
340340
>{ x: nt } : { x: number; }
@@ -375,7 +375,7 @@ function conditionalSpreadString(st: string): { x: string, y: number } {
375375
}
376376
let o2 = { ...st && { x: st }}
377377
>o2 : {}
378-
>{ ...st && { x: st }} : {} | { x: string; }
378+
>{ ...st && { x: st }} : { x: string; } | {}
379379
>st && { x: st } : "" | { x: string; }
380380
>st : string
381381
>{ x: st } : { x: string; }
Collapse file
+37Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [spreadOfObjectLiteralAssignableToIndexSignature.ts]
2+
const foo: Record<never, never> = {} // OK
3+
4+
interface RecordOfRecords extends Record<keyof any, RecordOfRecords> {}
5+
const recordOfRecords: RecordOfRecords = {}
6+
recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} // OK
7+
recordOfRecords.propB = {...(foo && {foo})} // OK
8+
recordOfRecords.propC = {...(foo !== undefined && {foo})} // error'd in 3.7 beta, should be OK
9+
10+
interface RecordOfRecordsOrEmpty extends Record<keyof any, RecordOfRecordsOrEmpty | {}> {}
11+
const recordsOfRecordsOrEmpty: RecordOfRecordsOrEmpty = {}
12+
recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} // OK
13+
recordsOfRecordsOrEmpty.propB = {...(foo && {foo})} // OK
14+
recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} // OK
15+
16+
//// [spreadOfObjectLiteralAssignableToIndexSignature.js]
17+
"use strict";
18+
var __assign = (this && this.__assign) || function () {
19+
__assign = Object.assign || function(t) {
20+
for (var s, i = 1, n = arguments.length; i < n; i++) {
21+
s = arguments[i];
22+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
23+
t[p] = s[p];
24+
}
25+
return t;
26+
};
27+
return __assign.apply(this, arguments);
28+
};
29+
var foo = {}; // OK
30+
var recordOfRecords = {};
31+
recordOfRecords.propA = __assign({}, (foo !== undefined ? { foo: foo } : {})); // OK
32+
recordOfRecords.propB = __assign({}, (foo && { foo: foo })); // OK
33+
recordOfRecords.propC = __assign({}, (foo !== undefined && { foo: foo })); // error'd in 3.7 beta, should be OK
34+
var recordsOfRecordsOrEmpty = {};
35+
recordsOfRecordsOrEmpty.propA = __assign({}, (foo !== undefined ? { foo: foo } : {})); // OK
36+
recordsOfRecordsOrEmpty.propB = __assign({}, (foo && { foo: foo })); // OK
37+
recordsOfRecordsOrEmpty.propC = __assign({}, (foo !== undefined && { foo: foo })); // OK
Collapse file
+57Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
=== tests/cases/compiler/spreadOfObjectLiteralAssignableToIndexSignature.ts ===
2+
const foo: Record<never, never> = {} // OK
3+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
4+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
5+
6+
interface RecordOfRecords extends Record<keyof any, RecordOfRecords> {}
7+
>RecordOfRecords : Symbol(RecordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 36))
8+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
9+
>RecordOfRecords : Symbol(RecordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 36))
10+
11+
const recordOfRecords: RecordOfRecords = {}
12+
>recordOfRecords : Symbol(recordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 3, 5))
13+
>RecordOfRecords : Symbol(RecordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 36))
14+
15+
recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} // OK
16+
>recordOfRecords : Symbol(recordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 3, 5))
17+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
18+
>undefined : Symbol(undefined)
19+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 4, 50))
20+
21+
recordOfRecords.propB = {...(foo && {foo})} // OK
22+
>recordOfRecords : Symbol(recordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 3, 5))
23+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
24+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 5, 37))
25+
26+
recordOfRecords.propC = {...(foo !== undefined && {foo})} // error'd in 3.7 beta, should be OK
27+
>recordOfRecords : Symbol(recordOfRecords, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 3, 5))
28+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
29+
>undefined : Symbol(undefined)
30+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 6, 51))
31+
32+
interface RecordOfRecordsOrEmpty extends Record<keyof any, RecordOfRecordsOrEmpty | {}> {}
33+
>RecordOfRecordsOrEmpty : Symbol(RecordOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 6, 57))
34+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
35+
>RecordOfRecordsOrEmpty : Symbol(RecordOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 6, 57))
36+
37+
const recordsOfRecordsOrEmpty: RecordOfRecordsOrEmpty = {}
38+
>recordsOfRecordsOrEmpty : Symbol(recordsOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 9, 5))
39+
>RecordOfRecordsOrEmpty : Symbol(RecordOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 6, 57))
40+
41+
recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} // OK
42+
>recordsOfRecordsOrEmpty : Symbol(recordsOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 9, 5))
43+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
44+
>undefined : Symbol(undefined)
45+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 10, 58))
46+
47+
recordsOfRecordsOrEmpty.propB = {...(foo && {foo})} // OK
48+
>recordsOfRecordsOrEmpty : Symbol(recordsOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 9, 5))
49+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
50+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 11, 45))
51+
52+
recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} // OK
53+
>recordsOfRecordsOrEmpty : Symbol(recordsOfRecordsOrEmpty, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 9, 5))
54+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 0, 5))
55+
>undefined : Symbol(undefined)
56+
>foo : Symbol(foo, Decl(spreadOfObjectLiteralAssignableToIndexSignature.ts, 12, 59))
57+
Collapse file
+97Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
=== tests/cases/compiler/spreadOfObjectLiteralAssignableToIndexSignature.ts ===
2+
const foo: Record<never, never> = {} // OK
3+
>foo : Record<never, never>
4+
>{} : {}
5+
6+
interface RecordOfRecords extends Record<keyof any, RecordOfRecords> {}
7+
const recordOfRecords: RecordOfRecords = {}
8+
>recordOfRecords : RecordOfRecords
9+
>{} : {}
10+
11+
recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} // OK
12+
>recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} : { foo: Record<never, never>; } | {}
13+
>recordOfRecords.propA : RecordOfRecords
14+
>recordOfRecords : RecordOfRecords
15+
>propA : RecordOfRecords
16+
>{...(foo !== undefined ? {foo} : {})} : { foo: Record<never, never>; } | {}
17+
>(foo !== undefined ? {foo} : {}) : { foo: Record<never, never>; } | {}
18+
>foo !== undefined ? {foo} : {} : { foo: Record<never, never>; } | {}
19+
>foo !== undefined : boolean
20+
>foo : Record<never, never>
21+
>undefined : undefined
22+
>{foo} : { foo: Record<never, never>; }
23+
>foo : Record<never, never>
24+
>{} : {}
25+
26+
recordOfRecords.propB = {...(foo && {foo})} // OK
27+
>recordOfRecords.propB = {...(foo && {foo})} : { foo: Record<never, never>; }
28+
>recordOfRecords.propB : RecordOfRecords
29+
>recordOfRecords : RecordOfRecords
30+
>propB : RecordOfRecords
31+
>{...(foo && {foo})} : { foo: Record<never, never>; }
32+
>(foo && {foo}) : { foo: Record<never, never>; }
33+
>foo && {foo} : { foo: Record<never, never>; }
34+
>foo : Record<never, never>
35+
>{foo} : { foo: Record<never, never>; }
36+
>foo : Record<never, never>
37+
38+
recordOfRecords.propC = {...(foo !== undefined && {foo})} // error'd in 3.7 beta, should be OK
39+
>recordOfRecords.propC = {...(foo !== undefined && {foo})} : { foo: Record<never, never>; } | {}
40+
>recordOfRecords.propC : RecordOfRecords
41+
>recordOfRecords : RecordOfRecords
42+
>propC : RecordOfRecords
43+
>{...(foo !== undefined && {foo})} : { foo: Record<never, never>; } | {}
44+
>(foo !== undefined && {foo}) : false | { foo: Record<never, never>; }
45+
>foo !== undefined && {foo} : false | { foo: Record<never, never>; }
46+
>foo !== undefined : boolean
47+
>foo : Record<never, never>
48+
>undefined : undefined
49+
>{foo} : { foo: Record<never, never>; }
50+
>foo : Record<never, never>
51+
52+
interface RecordOfRecordsOrEmpty extends Record<keyof any, RecordOfRecordsOrEmpty | {}> {}
53+
const recordsOfRecordsOrEmpty: RecordOfRecordsOrEmpty = {}
54+
>recordsOfRecordsOrEmpty : RecordOfRecordsOrEmpty
55+
>{} : {}
56+
57+
recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} // OK
58+
>recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} : { foo: Record<never, never>; } | {}
59+
>recordsOfRecordsOrEmpty.propA : {} | RecordOfRecordsOrEmpty
60+
>recordsOfRecordsOrEmpty : RecordOfRecordsOrEmpty
61+
>propA : {} | RecordOfRecordsOrEmpty
62+
>{...(foo !== undefined ? {foo} : {})} : { foo: Record<never, never>; } | {}
63+
>(foo !== undefined ? {foo} : {}) : { foo: Record<never, never>; } | {}
64+
>foo !== undefined ? {foo} : {} : { foo: Record<never, never>; } | {}
65+
>foo !== undefined : boolean
66+
>foo : Record<never, never>
67+
>undefined : undefined
68+
>{foo} : { foo: Record<never, never>; }
69+
>foo : Record<never, never>
70+
>{} : {}
71+
72+
recordsOfRecordsOrEmpty.propB = {...(foo && {foo})} // OK
73+
>recordsOfRecordsOrEmpty.propB = {...(foo && {foo})} : { foo: Record<never, never>; }
74+
>recordsOfRecordsOrEmpty.propB : {} | RecordOfRecordsOrEmpty
75+
>recordsOfRecordsOrEmpty : RecordOfRecordsOrEmpty
76+
>propB : {} | RecordOfRecordsOrEmpty
77+
>{...(foo && {foo})} : { foo: Record<never, never>; }
78+
>(foo && {foo}) : { foo: Record<never, never>; }
79+
>foo && {foo} : { foo: Record<never, never>; }
80+
>foo : Record<never, never>
81+
>{foo} : { foo: Record<never, never>; }
82+
>foo : Record<never, never>
83+
84+
recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} // OK
85+
>recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} : { foo: Record<never, never>; } | {}
86+
>recordsOfRecordsOrEmpty.propC : {} | RecordOfRecordsOrEmpty
87+
>recordsOfRecordsOrEmpty : RecordOfRecordsOrEmpty
88+
>propC : {} | RecordOfRecordsOrEmpty
89+
>{...(foo !== undefined && {foo})} : { foo: Record<never, never>; } | {}
90+
>(foo !== undefined && {foo}) : false | { foo: Record<never, never>; }
91+
>foo !== undefined && {foo} : false | { foo: Record<never, never>; }
92+
>foo !== undefined : boolean
93+
>foo : Record<never, never>
94+
>undefined : undefined
95+
>{foo} : { foo: Record<never, never>; }
96+
>foo : Record<never, never>
97+
Collapse file
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// @strict: true
2+
const foo: Record<never, never> = {} // OK
3+
4+
interface RecordOfRecords extends Record<keyof any, RecordOfRecords> {}
5+
const recordOfRecords: RecordOfRecords = {}
6+
recordOfRecords.propA = {...(foo !== undefined ? {foo} : {})} // OK
7+
recordOfRecords.propB = {...(foo && {foo})} // OK
8+
recordOfRecords.propC = {...(foo !== undefined && {foo})} // error'd in 3.7 beta, should be OK
9+
10+
interface RecordOfRecordsOrEmpty extends Record<keyof any, RecordOfRecordsOrEmpty | {}> {}
11+
const recordsOfRecordsOrEmpty: RecordOfRecordsOrEmpty = {}
12+
recordsOfRecordsOrEmpty.propA = {...(foo !== undefined ? {foo} : {})} // OK
13+
recordsOfRecordsOrEmpty.propB = {...(foo && {foo})} // OK
14+
recordsOfRecordsOrEmpty.propC = {...(foo !== undefined && {foo})} // OK

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.