Skip to content

Commit 445421b

Browse files
authored
Merge pull request microsoft#13772 from Microsoft/partialAndEmptyObjectType
Make empty object type assignable to any Partial<T>
2 parents ae92437 + 8659101 commit 445421b

File tree

5 files changed

+149
-51
lines changed

5 files changed

+149
-51
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7687,7 +7687,7 @@ namespace ts {
76877687
function isKnownProperty(type: Type, name: string): boolean {
76887688
if (type.flags & TypeFlags.Object) {
76897689
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
7690-
if ((relation === assignableRelation || relation === comparableRelation) && (type === globalObjectType || isEmptyObjectType(resolved)) ||
7690+
if ((relation === assignableRelation || relation === comparableRelation) && (type === globalObjectType || isEmptyResolvedType(resolved)) ||
76917691
resolved.stringIndexInfo ||
76927692
(resolved.numberIndexInfo && isNumericLiteralName(name)) ||
76937693
getPropertyOfType(type, name)) {
@@ -7704,14 +7704,18 @@ namespace ts {
77047704
return false;
77057705
}
77067706

7707-
function isEmptyObjectType(t: ResolvedType) {
7707+
function isEmptyResolvedType(t: ResolvedType) {
77087708
return t.properties.length === 0 &&
77097709
t.callSignatures.length === 0 &&
77107710
t.constructSignatures.length === 0 &&
77117711
!t.stringIndexInfo &&
77127712
!t.numberIndexInfo;
77137713
}
77147714

7715+
function isEmptyObjectType(type: Type) {
7716+
return type.flags & TypeFlags.Object && isEmptyResolvedType(resolveStructuredTypeMembers(<ObjectType>type));
7717+
}
7718+
77157719
function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
77167720
if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) {
77177721
for (const prop of getPropertiesOfObjectType(source)) {
@@ -7925,10 +7929,14 @@ namespace ts {
79257929
}
79267930
}
79277931
}
7932+
else if ((<MappedType>target).declaration.questionToken && isEmptyObjectType(source)) {
7933+
return Ternary.True;
7934+
7935+
}
79287936
}
79297937
else if (relation !== identityRelation) {
79307938
const resolved = resolveStructuredTypeMembers(<ObjectType>target);
7931-
if (isEmptyObjectType(resolved) || resolved.stringIndexInfo && resolved.stringIndexInfo.type.flags & TypeFlags.Any) {
7939+
if (isEmptyResolvedType(resolved) || resolved.stringIndexInfo && resolved.stringIndexInfo.type.flags & TypeFlags.Any) {
79327940
return Ternary.True;
79337941
}
79347942
}

tests/baselines/reference/mappedTypesAndObjects.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,37 @@ function f2<T>(x: Partial<T>, y: Readonly<T>) {
1212
obj = y;
1313
}
1414

15+
function f3<T>(x: Partial<T>) {
16+
x = {};
17+
}
18+
1519
// Repro from #12900
1620

1721
interface Base {
18-
foo: { [key: string]: any };
19-
bar: any;
20-
baz: any;
22+
foo: { [key: string]: any };
23+
bar: any;
24+
baz: any;
2125
}
2226

2327
interface E1<T> extends Base {
24-
foo: T;
28+
foo: T;
2529
}
2630

2731
interface Something { name: string, value: string };
2832
interface E2 extends Base {
29-
foo: Partial<Something>; // or other mapped type
33+
foo: Partial<Something>; // or other mapped type
3034
}
3135

3236
interface E3<T> extends Base {
33-
foo: Partial<T>; // or other mapped type
34-
}
37+
foo: Partial<T>; // or other mapped type
38+
}
39+
40+
// Repro from #13747
41+
42+
class Form<T> {
43+
private values: {[P in keyof T]?: T[P]} = {}
44+
}
45+
3546

3647
//// [mappedTypesAndObjects.js]
3748
function f1(x, y) {
@@ -44,12 +55,23 @@ function f2(x, y) {
4455
obj = x;
4556
obj = y;
4657
}
58+
function f3(x) {
59+
x = {};
60+
}
4761
;
62+
// Repro from #13747
63+
var Form = (function () {
64+
function Form() {
65+
this.values = {};
66+
}
67+
return Form;
68+
}());
4869

4970

5071
//// [mappedTypesAndObjects.d.ts]
5172
declare function f1<T>(x: Partial<T>, y: Readonly<T>): void;
5273
declare function f2<T>(x: Partial<T>, y: Readonly<T>): void;
74+
declare function f3<T>(x: Partial<T>): void;
5375
interface Base {
5476
foo: {
5577
[key: string]: any;
@@ -70,3 +92,6 @@ interface E2 extends Base {
7092
interface E3<T> extends Base {
7193
foo: Partial<T>;
7294
}
95+
declare class Form<T> {
96+
private values;
97+
}

tests/baselines/reference/mappedTypesAndObjects.symbols

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -45,54 +45,80 @@ function f2<T>(x: Partial<T>, y: Readonly<T>) {
4545
>y : Symbol(y, Decl(mappedTypesAndObjects.ts, 7, 29))
4646
}
4747

48+
function f3<T>(x: Partial<T>) {
49+
>f3 : Symbol(f3, Decl(mappedTypesAndObjects.ts, 11, 1))
50+
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 13, 12))
51+
>x : Symbol(x, Decl(mappedTypesAndObjects.ts, 13, 15))
52+
>Partial : Symbol(Partial, Decl(lib.d.ts, --, --))
53+
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 13, 12))
54+
55+
x = {};
56+
>x : Symbol(x, Decl(mappedTypesAndObjects.ts, 13, 15))
57+
}
58+
4859
// Repro from #12900
4960

5061
interface Base {
51-
>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 11, 1))
62+
>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 15, 1))
5263

53-
foo: { [key: string]: any };
54-
>foo : Symbol(Base.foo, Decl(mappedTypesAndObjects.ts, 15, 16))
55-
>key : Symbol(key, Decl(mappedTypesAndObjects.ts, 16, 11))
64+
foo: { [key: string]: any };
65+
>foo : Symbol(Base.foo, Decl(mappedTypesAndObjects.ts, 19, 16))
66+
>key : Symbol(key, Decl(mappedTypesAndObjects.ts, 20, 12))
5667

57-
bar: any;
58-
>bar : Symbol(Base.bar, Decl(mappedTypesAndObjects.ts, 16, 31))
68+
bar: any;
69+
>bar : Symbol(Base.bar, Decl(mappedTypesAndObjects.ts, 20, 32))
5970

60-
baz: any;
61-
>baz : Symbol(Base.baz, Decl(mappedTypesAndObjects.ts, 17, 12))
71+
baz: any;
72+
>baz : Symbol(Base.baz, Decl(mappedTypesAndObjects.ts, 21, 13))
6273
}
6374

6475
interface E1<T> extends Base {
65-
>E1 : Symbol(E1, Decl(mappedTypesAndObjects.ts, 19, 1))
66-
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 21, 13))
67-
>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 11, 1))
76+
>E1 : Symbol(E1, Decl(mappedTypesAndObjects.ts, 23, 1))
77+
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 25, 13))
78+
>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 15, 1))
6879

69-
foo: T;
70-
>foo : Symbol(E1.foo, Decl(mappedTypesAndObjects.ts, 21, 30))
71-
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 21, 13))
80+
foo: T;
81+
>foo : Symbol(E1.foo, Decl(mappedTypesAndObjects.ts, 25, 30))
82+
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 25, 13))
7283
}
7384

7485
interface Something { name: string, value: string };
75-
>Something : Symbol(Something, Decl(mappedTypesAndObjects.ts, 23, 1))
76-
>name : Symbol(Something.name, Decl(mappedTypesAndObjects.ts, 25, 21))
77-
>value : Symbol(Something.value, Decl(mappedTypesAndObjects.ts, 25, 35))
86+
>Something : Symbol(Something, Decl(mappedTypesAndObjects.ts, 27, 1))
87+
>name : Symbol(Something.name, Decl(mappedTypesAndObjects.ts, 29, 21))
88+
>value : Symbol(Something.value, Decl(mappedTypesAndObjects.ts, 29, 35))
7889

7990
interface E2 extends Base {
80-
>E2 : Symbol(E2, Decl(mappedTypesAndObjects.ts, 25, 52))
81-
>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 11, 1))
91+
>E2 : Symbol(E2, Decl(mappedTypesAndObjects.ts, 29, 52))
92+
>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 15, 1))
8293

83-
foo: Partial<Something>; // or other mapped type
84-
>foo : Symbol(E2.foo, Decl(mappedTypesAndObjects.ts, 26, 27))
94+
foo: Partial<Something>; // or other mapped type
95+
>foo : Symbol(E2.foo, Decl(mappedTypesAndObjects.ts, 30, 27))
8596
>Partial : Symbol(Partial, Decl(lib.d.ts, --, --))
86-
>Something : Symbol(Something, Decl(mappedTypesAndObjects.ts, 23, 1))
97+
>Something : Symbol(Something, Decl(mappedTypesAndObjects.ts, 27, 1))
8798
}
8899

89100
interface E3<T> extends Base {
90-
>E3 : Symbol(E3, Decl(mappedTypesAndObjects.ts, 28, 1))
91-
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 30, 13))
92-
>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 11, 1))
101+
>E3 : Symbol(E3, Decl(mappedTypesAndObjects.ts, 32, 1))
102+
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 34, 13))
103+
>Base : Symbol(Base, Decl(mappedTypesAndObjects.ts, 15, 1))
93104

94-
foo: Partial<T>; // or other mapped type
95-
>foo : Symbol(E3.foo, Decl(mappedTypesAndObjects.ts, 30, 30))
105+
foo: Partial<T>; // or other mapped type
106+
>foo : Symbol(E3.foo, Decl(mappedTypesAndObjects.ts, 34, 30))
96107
>Partial : Symbol(Partial, Decl(lib.d.ts, --, --))
97-
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 30, 13))
108+
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 34, 13))
98109
}
110+
111+
// Repro from #13747
112+
113+
class Form<T> {
114+
>Form : Symbol(Form, Decl(mappedTypesAndObjects.ts, 36, 1))
115+
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 40, 11))
116+
117+
private values: {[P in keyof T]?: T[P]} = {}
118+
>values : Symbol(Form.values, Decl(mappedTypesAndObjects.ts, 40, 15))
119+
>P : Symbol(P, Decl(mappedTypesAndObjects.ts, 41, 22))
120+
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 40, 11))
121+
>T : Symbol(T, Decl(mappedTypesAndObjects.ts, 40, 11))
122+
>P : Symbol(P, Decl(mappedTypesAndObjects.ts, 41, 22))
123+
}
124+

tests/baselines/reference/mappedTypesAndObjects.types

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,32 @@ function f2<T>(x: Partial<T>, y: Readonly<T>) {
4949
>y : Readonly<T>
5050
}
5151

52+
function f3<T>(x: Partial<T>) {
53+
>f3 : <T>(x: Partial<T>) => void
54+
>T : T
55+
>x : Partial<T>
56+
>Partial : Partial<T>
57+
>T : T
58+
59+
x = {};
60+
>x = {} : {}
61+
>x : Partial<T>
62+
>{} : {}
63+
}
64+
5265
// Repro from #12900
5366

5467
interface Base {
5568
>Base : Base
5669

57-
foo: { [key: string]: any };
70+
foo: { [key: string]: any };
5871
>foo : { [key: string]: any; }
5972
>key : string
6073

61-
bar: any;
74+
bar: any;
6275
>bar : any
6376

64-
baz: any;
77+
baz: any;
6578
>baz : any
6679
}
6780

@@ -70,7 +83,7 @@ interface E1<T> extends Base {
7083
>T : T
7184
>Base : Base
7285

73-
foo: T;
86+
foo: T;
7487
>foo : T
7588
>T : T
7689
}
@@ -84,7 +97,7 @@ interface E2 extends Base {
8497
>E2 : E2
8598
>Base : Base
8699

87-
foo: Partial<Something>; // or other mapped type
100+
foo: Partial<Something>; // or other mapped type
88101
>foo : Partial<Something>
89102
>Partial : Partial<T>
90103
>Something : Something
@@ -95,8 +108,24 @@ interface E3<T> extends Base {
95108
>T : T
96109
>Base : Base
97110

98-
foo: Partial<T>; // or other mapped type
111+
foo: Partial<T>; // or other mapped type
99112
>foo : Partial<T>
100113
>Partial : Partial<T>
101114
>T : T
102115
}
116+
117+
// Repro from #13747
118+
119+
class Form<T> {
120+
>Form : Form<T>
121+
>T : T
122+
123+
private values: {[P in keyof T]?: T[P]} = {}
124+
>values : { [P in keyof T]?: T[P] | undefined; }
125+
>P : P
126+
>T : T
127+
>T : T
128+
>P : P
129+
>{} : {}
130+
}
131+

tests/cases/conformance/types/mapped/mappedTypesAndObjects.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,33 @@ function f2<T>(x: Partial<T>, y: Readonly<T>) {
1313
obj = y;
1414
}
1515

16+
function f3<T>(x: Partial<T>) {
17+
x = {};
18+
}
19+
1620
// Repro from #12900
1721

1822
interface Base {
19-
foo: { [key: string]: any };
20-
bar: any;
21-
baz: any;
23+
foo: { [key: string]: any };
24+
bar: any;
25+
baz: any;
2226
}
2327

2428
interface E1<T> extends Base {
25-
foo: T;
29+
foo: T;
2630
}
2731

2832
interface Something { name: string, value: string };
2933
interface E2 extends Base {
30-
foo: Partial<Something>; // or other mapped type
34+
foo: Partial<Something>; // or other mapped type
3135
}
3236

3337
interface E3<T> extends Base {
34-
foo: Partial<T>; // or other mapped type
35-
}
38+
foo: Partial<T>; // or other mapped type
39+
}
40+
41+
// Repro from #13747
42+
43+
class Form<T> {
44+
private values: {[P in keyof T]?: T[P]} = {}
45+
}

0 commit comments

Comments
 (0)