Skip to content

Commit

Permalink
Fresh {} is subtype of object (#49503)
Browse files Browse the repository at this point in the history
* Fresh {} is subtype of object

* Add regression test
  • Loading branch information
ahejlsberg committed Jun 13, 2022
1 parent 2ecde27 commit dc6a80b
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 2 deletions.
4 changes: 2 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18189,7 +18189,7 @@ namespace ts {
// Since unions and intersections may reduce to `never`, we exclude them here.
if (s & TypeFlags.Undefined && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & (TypeFlags.Undefined | TypeFlags.Void))) return true;
if (s & TypeFlags.Null && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & TypeFlags.Null)) return true;
if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive && !(relation === strictSubtypeRelation && isEmptyAnonymousObjectType(source))) return true;
if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive && !(relation === strictSubtypeRelation && isEmptyAnonymousObjectType(source) && !(getObjectFlags(source) & ObjectFlags.FreshLiteral))) return true;
if (relation === assignableRelation || relation === comparableRelation) {
if (s & TypeFlags.Any) return true;
// Type number or any numeric literal type is assignable to any numeric enum type or any
Expand Down Expand Up @@ -18935,7 +18935,7 @@ namespace ts {
return typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target as UnionType, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
}
if (target.flags & TypeFlags.Intersection) {
return typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, IntersectionState.Target);
return typeRelatedToEachType(source, target as IntersectionType, reportErrors, IntersectionState.Target);
}
// Source is an intersection. For the comparable relation, if the target is a primitive type we hoist the
// constraints of all non-primitive types in the source into a new intersection. We do this because the
Expand Down
32 changes: 32 additions & 0 deletions tests/baselines/reference/nonPrimitiveAndEmptyObject.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//// [nonPrimitiveAndEmptyObject.ts]
// Repro from #49480

export interface BarProps {
barProp?: string;
}

export interface FooProps {
fooProps?: BarProps & object;
}

declare const foo: FooProps;
const { fooProps = {} } = foo;

fooProps.barProp;


//// [nonPrimitiveAndEmptyObject.js]
"use strict";
// Repro from #49480
exports.__esModule = true;
var _a = foo.fooProps, fooProps = _a === void 0 ? {} : _a;
fooProps.barProp;


//// [nonPrimitiveAndEmptyObject.d.ts]
export interface BarProps {
barProp?: string;
}
export interface FooProps {
fooProps?: BarProps & object;
}
31 changes: 31 additions & 0 deletions tests/baselines/reference/nonPrimitiveAndEmptyObject.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
=== tests/cases/conformance/types/nonPrimitive/nonPrimitiveAndEmptyObject.ts ===
// Repro from #49480

export interface BarProps {
>BarProps : Symbol(BarProps, Decl(nonPrimitiveAndEmptyObject.ts, 0, 0))

barProp?: string;
>barProp : Symbol(BarProps.barProp, Decl(nonPrimitiveAndEmptyObject.ts, 2, 27))
}

export interface FooProps {
>FooProps : Symbol(FooProps, Decl(nonPrimitiveAndEmptyObject.ts, 4, 1))

fooProps?: BarProps & object;
>fooProps : Symbol(FooProps.fooProps, Decl(nonPrimitiveAndEmptyObject.ts, 6, 27))
>BarProps : Symbol(BarProps, Decl(nonPrimitiveAndEmptyObject.ts, 0, 0))
}

declare const foo: FooProps;
>foo : Symbol(foo, Decl(nonPrimitiveAndEmptyObject.ts, 10, 13))
>FooProps : Symbol(FooProps, Decl(nonPrimitiveAndEmptyObject.ts, 4, 1))

const { fooProps = {} } = foo;
>fooProps : Symbol(fooProps, Decl(nonPrimitiveAndEmptyObject.ts, 11, 7))
>foo : Symbol(foo, Decl(nonPrimitiveAndEmptyObject.ts, 10, 13))

fooProps.barProp;
>fooProps.barProp : Symbol(BarProps.barProp, Decl(nonPrimitiveAndEmptyObject.ts, 2, 27))
>fooProps : Symbol(fooProps, Decl(nonPrimitiveAndEmptyObject.ts, 11, 7))
>barProp : Symbol(BarProps.barProp, Decl(nonPrimitiveAndEmptyObject.ts, 2, 27))

26 changes: 26 additions & 0 deletions tests/baselines/reference/nonPrimitiveAndEmptyObject.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=== tests/cases/conformance/types/nonPrimitive/nonPrimitiveAndEmptyObject.ts ===
// Repro from #49480

export interface BarProps {
barProp?: string;
>barProp : string | undefined
}

export interface FooProps {
fooProps?: BarProps & object;
>fooProps : (BarProps & object) | undefined
}

declare const foo: FooProps;
>foo : FooProps

const { fooProps = {} } = foo;
>fooProps : BarProps & object
>{} : {}
>foo : FooProps

fooProps.barProp;
>fooProps.barProp : string | undefined
>fooProps : BarProps & object
>barProp : string | undefined

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// @strict: true
// @declaration: true

// Repro from #49480

export interface BarProps {
barProp?: string;
}

export interface FooProps {
fooProps?: BarProps & object;
}

declare const foo: FooProps;
const { fooProps = {} } = foo;

fooProps.barProp;

0 comments on commit dc6a80b

Please sign in to comment.