Skip to content

Commit a2205ad

Browse files
authored
Merge pull request #28429 from Microsoft/fixEmptyObjectIntersection
Fix empty object intersections
2 parents 15d28d6 + 187fa20 commit a2205ad

File tree

6 files changed

+457
-22
lines changed

6 files changed

+457
-22
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9097,8 +9097,11 @@ namespace ts {
90979097
if (flags & TypeFlags.Intersection) {
90989098
return addTypesToIntersection(typeSet, includes, (<IntersectionType>type).types);
90999099
}
9100-
if (getObjectFlags(type) & ObjectFlags.Anonymous && isEmptyObjectType(type)) {
9101-
includes |= TypeFlags.EmptyObject;
9100+
if (isEmptyAnonymousObjectType(type)) {
9101+
if (!(includes & TypeFlags.EmptyObject)) {
9102+
includes |= TypeFlags.EmptyObject;
9103+
typeSet.push(type);
9104+
}
91029105
}
91039106
else {
91049107
includes |= flags & ~TypeFlags.ConstructionFlags;
@@ -9229,8 +9232,8 @@ namespace ts {
92299232
includes & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol) {
92309233
removeRedundantPrimitiveTypes(typeSet, includes);
92319234
}
9232-
if (includes & TypeFlags.EmptyObject && !(includes & TypeFlags.Object)) {
9233-
typeSet.push(emptyObjectType);
9235+
if (includes & TypeFlags.EmptyObject && includes & TypeFlags.Object) {
9236+
orderedRemoveItemAt(typeSet, findIndex(typeSet, isEmptyAnonymousObjectType));
92349237
}
92359238
if (typeSet.length === 0) {
92369239
return unknownType;
@@ -11277,6 +11280,10 @@ namespace ts {
1127711280
false;
1127811281
}
1127911282

11283+
function isEmptyAnonymousObjectType(type: Type) {
11284+
return !!(getObjectFlags(type) & ObjectFlags.Anonymous) && isEmptyObjectType(type);
11285+
}
11286+
1128011287
function isEnumTypeRelatedTo(sourceSymbol: Symbol, targetSymbol: Symbol, errorReporter?: ErrorReporter) {
1128111288
if (sourceSymbol === targetSymbol) {
1128211289
return true;

tests/baselines/reference/intersectionsAndEmptyObjects.js

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,95 @@ let x11: C & D;
2424
let x12: A & B & C & D;
2525
let x13: D & E;
2626
let x14: A & B & C & D & E;
27+
28+
// Repro from #20225
29+
30+
type Dictionary = { [name: string]: string };
31+
32+
const intersectDictionaries = <F1 extends Dictionary, F2 extends Dictionary>(
33+
d1: F1,
34+
d2: F2,
35+
): F1 & F2 => Object.assign({}, d1, d2);
36+
37+
const testDictionary = <T extends Dictionary>(_value: T) => { };
38+
39+
const d1 = {};
40+
testDictionary(d1);
41+
const d2 = intersectDictionaries(d1, d1);
42+
testDictionary(d2);
43+
44+
const d3 = {
45+
s: '',
46+
};
47+
testDictionary(d3);
48+
const d4 = intersectDictionaries(d1, d3);
49+
testDictionary(d4);
50+
const d5 = intersectDictionaries(d3, d1);
51+
testDictionary(d5);
52+
const d6 = intersectDictionaries(d3, d3);
53+
testDictionary(d6);
54+
55+
// Repro from #27044
56+
57+
type choices<IChoiceList extends {
58+
[key: string]: boolean;
59+
}> = IChoiceList & {
60+
shoes:boolean;
61+
food:boolean;
62+
};
63+
64+
type IMyChoiceList = {
65+
car: true
66+
};
67+
68+
type IUnknownChoiceList = {};
69+
70+
var defaultChoices: choices<{}>;
71+
var defaultChoicesAndEmpty: choices<{} & {}>;
72+
73+
var myChoices: choices<IMyChoiceList>;
74+
var myChoicesAndEmpty: choices<IMyChoiceList & {}>;
75+
76+
var unknownChoices: choices<IUnknownChoiceList>;
77+
var unknownChoicesAndEmpty: choices<IUnknownChoiceList & {}>;
2778

2879

2980
//// [intersectionsAndEmptyObjects.js]
3081
// Empty object type literals are removed from intersections types
3182
// that contain other object types
32-
var x01;
33-
var x02;
34-
var x03;
35-
var x04;
36-
var x05;
37-
var x06;
38-
var x07;
39-
var x08;
40-
var x09;
41-
var x10;
42-
var x11;
43-
var x12;
44-
var x13;
45-
var x14;
83+
let x01;
84+
let x02;
85+
let x03;
86+
let x04;
87+
let x05;
88+
let x06;
89+
let x07;
90+
let x08;
91+
let x09;
92+
let x10;
93+
let x11;
94+
let x12;
95+
let x13;
96+
let x14;
97+
const intersectDictionaries = (d1, d2) => Object.assign({}, d1, d2);
98+
const testDictionary = (_value) => { };
99+
const d1 = {};
100+
testDictionary(d1);
101+
const d2 = intersectDictionaries(d1, d1);
102+
testDictionary(d2);
103+
const d3 = {
104+
s: '',
105+
};
106+
testDictionary(d3);
107+
const d4 = intersectDictionaries(d1, d3);
108+
testDictionary(d4);
109+
const d5 = intersectDictionaries(d3, d1);
110+
testDictionary(d5);
111+
const d6 = intersectDictionaries(d3, d3);
112+
testDictionary(d6);
113+
var defaultChoices;
114+
var defaultChoicesAndEmpty;
115+
var myChoices;
116+
var myChoicesAndEmpty;
117+
var unknownChoices;
118+
var unknownChoicesAndEmpty;

tests/baselines/reference/intersectionsAndEmptyObjects.symbols

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,157 @@ let x14: A & B & C & D & E;
9292
>D : Symbol(D, Decl(intersectionsAndEmptyObjects.ts, 15, 39))
9393
>E : Symbol(E, Decl(intersectionsAndEmptyObjects.ts, 17, 14))
9494

95+
// Repro from #20225
96+
97+
type Dictionary = { [name: string]: string };
98+
>Dictionary : Symbol(Dictionary, Decl(intersectionsAndEmptyObjects.ts, 24, 27))
99+
>name : Symbol(name, Decl(intersectionsAndEmptyObjects.ts, 28, 21))
100+
101+
const intersectDictionaries = <F1 extends Dictionary, F2 extends Dictionary>(
102+
>intersectDictionaries : Symbol(intersectDictionaries, Decl(intersectionsAndEmptyObjects.ts, 30, 5))
103+
>F1 : Symbol(F1, Decl(intersectionsAndEmptyObjects.ts, 30, 31))
104+
>Dictionary : Symbol(Dictionary, Decl(intersectionsAndEmptyObjects.ts, 24, 27))
105+
>F2 : Symbol(F2, Decl(intersectionsAndEmptyObjects.ts, 30, 53))
106+
>Dictionary : Symbol(Dictionary, Decl(intersectionsAndEmptyObjects.ts, 24, 27))
107+
108+
d1: F1,
109+
>d1 : Symbol(d1, Decl(intersectionsAndEmptyObjects.ts, 30, 77))
110+
>F1 : Symbol(F1, Decl(intersectionsAndEmptyObjects.ts, 30, 31))
111+
112+
d2: F2,
113+
>d2 : Symbol(d2, Decl(intersectionsAndEmptyObjects.ts, 31, 9))
114+
>F2 : Symbol(F2, Decl(intersectionsAndEmptyObjects.ts, 30, 53))
115+
116+
): F1 & F2 => Object.assign({}, d1, d2);
117+
>F1 : Symbol(F1, Decl(intersectionsAndEmptyObjects.ts, 30, 31))
118+
>F2 : Symbol(F2, Decl(intersectionsAndEmptyObjects.ts, 30, 53))
119+
>Object.assign : Symbol(ObjectConstructor.assign, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
120+
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
121+
>assign : Symbol(ObjectConstructor.assign, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
122+
>d1 : Symbol(d1, Decl(intersectionsAndEmptyObjects.ts, 30, 77))
123+
>d2 : Symbol(d2, Decl(intersectionsAndEmptyObjects.ts, 31, 9))
124+
125+
const testDictionary = <T extends Dictionary>(_value: T) => { };
126+
>testDictionary : Symbol(testDictionary, Decl(intersectionsAndEmptyObjects.ts, 35, 5))
127+
>T : Symbol(T, Decl(intersectionsAndEmptyObjects.ts, 35, 24))
128+
>Dictionary : Symbol(Dictionary, Decl(intersectionsAndEmptyObjects.ts, 24, 27))
129+
>_value : Symbol(_value, Decl(intersectionsAndEmptyObjects.ts, 35, 46))
130+
>T : Symbol(T, Decl(intersectionsAndEmptyObjects.ts, 35, 24))
131+
132+
const d1 = {};
133+
>d1 : Symbol(d1, Decl(intersectionsAndEmptyObjects.ts, 37, 5))
134+
135+
testDictionary(d1);
136+
>testDictionary : Symbol(testDictionary, Decl(intersectionsAndEmptyObjects.ts, 35, 5))
137+
>d1 : Symbol(d1, Decl(intersectionsAndEmptyObjects.ts, 37, 5))
138+
139+
const d2 = intersectDictionaries(d1, d1);
140+
>d2 : Symbol(d2, Decl(intersectionsAndEmptyObjects.ts, 39, 5))
141+
>intersectDictionaries : Symbol(intersectDictionaries, Decl(intersectionsAndEmptyObjects.ts, 30, 5))
142+
>d1 : Symbol(d1, Decl(intersectionsAndEmptyObjects.ts, 37, 5))
143+
>d1 : Symbol(d1, Decl(intersectionsAndEmptyObjects.ts, 37, 5))
144+
145+
testDictionary(d2);
146+
>testDictionary : Symbol(testDictionary, Decl(intersectionsAndEmptyObjects.ts, 35, 5))
147+
>d2 : Symbol(d2, Decl(intersectionsAndEmptyObjects.ts, 39, 5))
148+
149+
const d3 = {
150+
>d3 : Symbol(d3, Decl(intersectionsAndEmptyObjects.ts, 42, 5))
151+
152+
s: '',
153+
>s : Symbol(s, Decl(intersectionsAndEmptyObjects.ts, 42, 12))
154+
155+
};
156+
testDictionary(d3);
157+
>testDictionary : Symbol(testDictionary, Decl(intersectionsAndEmptyObjects.ts, 35, 5))
158+
>d3 : Symbol(d3, Decl(intersectionsAndEmptyObjects.ts, 42, 5))
159+
160+
const d4 = intersectDictionaries(d1, d3);
161+
>d4 : Symbol(d4, Decl(intersectionsAndEmptyObjects.ts, 46, 5))
162+
>intersectDictionaries : Symbol(intersectDictionaries, Decl(intersectionsAndEmptyObjects.ts, 30, 5))
163+
>d1 : Symbol(d1, Decl(intersectionsAndEmptyObjects.ts, 37, 5))
164+
>d3 : Symbol(d3, Decl(intersectionsAndEmptyObjects.ts, 42, 5))
165+
166+
testDictionary(d4);
167+
>testDictionary : Symbol(testDictionary, Decl(intersectionsAndEmptyObjects.ts, 35, 5))
168+
>d4 : Symbol(d4, Decl(intersectionsAndEmptyObjects.ts, 46, 5))
169+
170+
const d5 = intersectDictionaries(d3, d1);
171+
>d5 : Symbol(d5, Decl(intersectionsAndEmptyObjects.ts, 48, 5))
172+
>intersectDictionaries : Symbol(intersectDictionaries, Decl(intersectionsAndEmptyObjects.ts, 30, 5))
173+
>d3 : Symbol(d3, Decl(intersectionsAndEmptyObjects.ts, 42, 5))
174+
>d1 : Symbol(d1, Decl(intersectionsAndEmptyObjects.ts, 37, 5))
175+
176+
testDictionary(d5);
177+
>testDictionary : Symbol(testDictionary, Decl(intersectionsAndEmptyObjects.ts, 35, 5))
178+
>d5 : Symbol(d5, Decl(intersectionsAndEmptyObjects.ts, 48, 5))
179+
180+
const d6 = intersectDictionaries(d3, d3);
181+
>d6 : Symbol(d6, Decl(intersectionsAndEmptyObjects.ts, 50, 5))
182+
>intersectDictionaries : Symbol(intersectDictionaries, Decl(intersectionsAndEmptyObjects.ts, 30, 5))
183+
>d3 : Symbol(d3, Decl(intersectionsAndEmptyObjects.ts, 42, 5))
184+
>d3 : Symbol(d3, Decl(intersectionsAndEmptyObjects.ts, 42, 5))
185+
186+
testDictionary(d6);
187+
>testDictionary : Symbol(testDictionary, Decl(intersectionsAndEmptyObjects.ts, 35, 5))
188+
>d6 : Symbol(d6, Decl(intersectionsAndEmptyObjects.ts, 50, 5))
189+
190+
// Repro from #27044
191+
192+
type choices<IChoiceList extends {
193+
>choices : Symbol(choices, Decl(intersectionsAndEmptyObjects.ts, 51, 19))
194+
>IChoiceList : Symbol(IChoiceList, Decl(intersectionsAndEmptyObjects.ts, 55, 13))
195+
196+
[key: string]: boolean;
197+
>key : Symbol(key, Decl(intersectionsAndEmptyObjects.ts, 56, 5))
198+
199+
}> = IChoiceList & {
200+
>IChoiceList : Symbol(IChoiceList, Decl(intersectionsAndEmptyObjects.ts, 55, 13))
201+
202+
shoes:boolean;
203+
>shoes : Symbol(shoes, Decl(intersectionsAndEmptyObjects.ts, 57, 20))
204+
205+
food:boolean;
206+
>food : Symbol(food, Decl(intersectionsAndEmptyObjects.ts, 58, 18))
207+
208+
};
209+
210+
type IMyChoiceList = {
211+
>IMyChoiceList : Symbol(IMyChoiceList, Decl(intersectionsAndEmptyObjects.ts, 60, 2))
212+
213+
car: true
214+
>car : Symbol(car, Decl(intersectionsAndEmptyObjects.ts, 62, 22))
215+
216+
};
217+
218+
type IUnknownChoiceList = {};
219+
>IUnknownChoiceList : Symbol(IUnknownChoiceList, Decl(intersectionsAndEmptyObjects.ts, 64, 2))
220+
221+
var defaultChoices: choices<{}>;
222+
>defaultChoices : Symbol(defaultChoices, Decl(intersectionsAndEmptyObjects.ts, 68, 3))
223+
>choices : Symbol(choices, Decl(intersectionsAndEmptyObjects.ts, 51, 19))
224+
225+
var defaultChoicesAndEmpty: choices<{} & {}>;
226+
>defaultChoicesAndEmpty : Symbol(defaultChoicesAndEmpty, Decl(intersectionsAndEmptyObjects.ts, 69, 3))
227+
>choices : Symbol(choices, Decl(intersectionsAndEmptyObjects.ts, 51, 19))
228+
229+
var myChoices: choices<IMyChoiceList>;
230+
>myChoices : Symbol(myChoices, Decl(intersectionsAndEmptyObjects.ts, 71, 3))
231+
>choices : Symbol(choices, Decl(intersectionsAndEmptyObjects.ts, 51, 19))
232+
>IMyChoiceList : Symbol(IMyChoiceList, Decl(intersectionsAndEmptyObjects.ts, 60, 2))
233+
234+
var myChoicesAndEmpty: choices<IMyChoiceList & {}>;
235+
>myChoicesAndEmpty : Symbol(myChoicesAndEmpty, Decl(intersectionsAndEmptyObjects.ts, 72, 3))
236+
>choices : Symbol(choices, Decl(intersectionsAndEmptyObjects.ts, 51, 19))
237+
>IMyChoiceList : Symbol(IMyChoiceList, Decl(intersectionsAndEmptyObjects.ts, 60, 2))
238+
239+
var unknownChoices: choices<IUnknownChoiceList>;
240+
>unknownChoices : Symbol(unknownChoices, Decl(intersectionsAndEmptyObjects.ts, 74, 3))
241+
>choices : Symbol(choices, Decl(intersectionsAndEmptyObjects.ts, 51, 19))
242+
>IUnknownChoiceList : Symbol(IUnknownChoiceList, Decl(intersectionsAndEmptyObjects.ts, 64, 2))
243+
244+
var unknownChoicesAndEmpty: choices<IUnknownChoiceList & {}>;
245+
>unknownChoicesAndEmpty : Symbol(unknownChoicesAndEmpty, Decl(intersectionsAndEmptyObjects.ts, 75, 3))
246+
>choices : Symbol(choices, Decl(intersectionsAndEmptyObjects.ts, 51, 19))
247+
>IUnknownChoiceList : Symbol(IUnknownChoiceList, Decl(intersectionsAndEmptyObjects.ts, 64, 2))
248+

0 commit comments

Comments
 (0)