Skip to content

Commit dc6a80b

Browse files
authored
Fresh {} is subtype of object (#49503)
* Fresh {} is subtype of object * Add regression test
1 parent 2ecde27 commit dc6a80b

File tree

5 files changed

+108
-2
lines changed

5 files changed

+108
-2
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18189,7 +18189,7 @@ namespace ts {
1818918189
// Since unions and intersections may reduce to `never`, we exclude them here.
1819018190
if (s & TypeFlags.Undefined && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & (TypeFlags.Undefined | TypeFlags.Void))) return true;
1819118191
if (s & TypeFlags.Null && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & TypeFlags.Null)) return true;
18192-
if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive && !(relation === strictSubtypeRelation && isEmptyAnonymousObjectType(source))) return true;
18192+
if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive && !(relation === strictSubtypeRelation && isEmptyAnonymousObjectType(source) && !(getObjectFlags(source) & ObjectFlags.FreshLiteral))) return true;
1819318193
if (relation === assignableRelation || relation === comparableRelation) {
1819418194
if (s & TypeFlags.Any) return true;
1819518195
// Type number or any numeric literal type is assignable to any numeric enum type or any
@@ -18935,7 +18935,7 @@ namespace ts {
1893518935
return typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target as UnionType, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
1893618936
}
1893718937
if (target.flags & TypeFlags.Intersection) {
18938-
return typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, IntersectionState.Target);
18938+
return typeRelatedToEachType(source, target as IntersectionType, reportErrors, IntersectionState.Target);
1893918939
}
1894018940
// Source is an intersection. For the comparable relation, if the target is a primitive type we hoist the
1894118941
// constraints of all non-primitive types in the source into a new intersection. We do this because the
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//// [nonPrimitiveAndEmptyObject.ts]
2+
// Repro from #49480
3+
4+
export interface BarProps {
5+
barProp?: string;
6+
}
7+
8+
export interface FooProps {
9+
fooProps?: BarProps & object;
10+
}
11+
12+
declare const foo: FooProps;
13+
const { fooProps = {} } = foo;
14+
15+
fooProps.barProp;
16+
17+
18+
//// [nonPrimitiveAndEmptyObject.js]
19+
"use strict";
20+
// Repro from #49480
21+
exports.__esModule = true;
22+
var _a = foo.fooProps, fooProps = _a === void 0 ? {} : _a;
23+
fooProps.barProp;
24+
25+
26+
//// [nonPrimitiveAndEmptyObject.d.ts]
27+
export interface BarProps {
28+
barProp?: string;
29+
}
30+
export interface FooProps {
31+
fooProps?: BarProps & object;
32+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/conformance/types/nonPrimitive/nonPrimitiveAndEmptyObject.ts ===
2+
// Repro from #49480
3+
4+
export interface BarProps {
5+
>BarProps : Symbol(BarProps, Decl(nonPrimitiveAndEmptyObject.ts, 0, 0))
6+
7+
barProp?: string;
8+
>barProp : Symbol(BarProps.barProp, Decl(nonPrimitiveAndEmptyObject.ts, 2, 27))
9+
}
10+
11+
export interface FooProps {
12+
>FooProps : Symbol(FooProps, Decl(nonPrimitiveAndEmptyObject.ts, 4, 1))
13+
14+
fooProps?: BarProps & object;
15+
>fooProps : Symbol(FooProps.fooProps, Decl(nonPrimitiveAndEmptyObject.ts, 6, 27))
16+
>BarProps : Symbol(BarProps, Decl(nonPrimitiveAndEmptyObject.ts, 0, 0))
17+
}
18+
19+
declare const foo: FooProps;
20+
>foo : Symbol(foo, Decl(nonPrimitiveAndEmptyObject.ts, 10, 13))
21+
>FooProps : Symbol(FooProps, Decl(nonPrimitiveAndEmptyObject.ts, 4, 1))
22+
23+
const { fooProps = {} } = foo;
24+
>fooProps : Symbol(fooProps, Decl(nonPrimitiveAndEmptyObject.ts, 11, 7))
25+
>foo : Symbol(foo, Decl(nonPrimitiveAndEmptyObject.ts, 10, 13))
26+
27+
fooProps.barProp;
28+
>fooProps.barProp : Symbol(BarProps.barProp, Decl(nonPrimitiveAndEmptyObject.ts, 2, 27))
29+
>fooProps : Symbol(fooProps, Decl(nonPrimitiveAndEmptyObject.ts, 11, 7))
30+
>barProp : Symbol(BarProps.barProp, Decl(nonPrimitiveAndEmptyObject.ts, 2, 27))
31+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/conformance/types/nonPrimitive/nonPrimitiveAndEmptyObject.ts ===
2+
// Repro from #49480
3+
4+
export interface BarProps {
5+
barProp?: string;
6+
>barProp : string | undefined
7+
}
8+
9+
export interface FooProps {
10+
fooProps?: BarProps & object;
11+
>fooProps : (BarProps & object) | undefined
12+
}
13+
14+
declare const foo: FooProps;
15+
>foo : FooProps
16+
17+
const { fooProps = {} } = foo;
18+
>fooProps : BarProps & object
19+
>{} : {}
20+
>foo : FooProps
21+
22+
fooProps.barProp;
23+
>fooProps.barProp : string | undefined
24+
>fooProps : BarProps & object
25+
>barProp : string | undefined
26+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// @strict: true
2+
// @declaration: true
3+
4+
// Repro from #49480
5+
6+
export interface BarProps {
7+
barProp?: string;
8+
}
9+
10+
export interface FooProps {
11+
fooProps?: BarProps & object;
12+
}
13+
14+
declare const foo: FooProps;
15+
const { fooProps = {} } = foo;
16+
17+
fooProps.barProp;

0 commit comments

Comments
 (0)