From a3bac963af43250592803500c1641bd088da67a4 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 12 Jun 2022 07:59:52 -0700 Subject: [PATCH 1/2] Fresh {} is subtype of object --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 89b23dc7ac66a..7badc5d57b92e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18232,7 +18232,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 @@ -18978,7 +18978,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 From 26df65ad0caa014cb94f1f6426905f3abba3cc46 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 12 Jun 2022 08:19:46 -0700 Subject: [PATCH 2/2] Add regression test --- .../reference/nonPrimitiveAndEmptyObject.js | 32 +++++++++++++++++++ .../nonPrimitiveAndEmptyObject.symbols | 31 ++++++++++++++++++ .../nonPrimitiveAndEmptyObject.types | 26 +++++++++++++++ .../nonPrimitiveAndEmptyObject.ts | 17 ++++++++++ 4 files changed, 106 insertions(+) create mode 100644 tests/baselines/reference/nonPrimitiveAndEmptyObject.js create mode 100644 tests/baselines/reference/nonPrimitiveAndEmptyObject.symbols create mode 100644 tests/baselines/reference/nonPrimitiveAndEmptyObject.types create mode 100644 tests/cases/conformance/types/nonPrimitive/nonPrimitiveAndEmptyObject.ts diff --git a/tests/baselines/reference/nonPrimitiveAndEmptyObject.js b/tests/baselines/reference/nonPrimitiveAndEmptyObject.js new file mode 100644 index 0000000000000..7cbea88a4ab02 --- /dev/null +++ b/tests/baselines/reference/nonPrimitiveAndEmptyObject.js @@ -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; +} diff --git a/tests/baselines/reference/nonPrimitiveAndEmptyObject.symbols b/tests/baselines/reference/nonPrimitiveAndEmptyObject.symbols new file mode 100644 index 0000000000000..5798edd686420 --- /dev/null +++ b/tests/baselines/reference/nonPrimitiveAndEmptyObject.symbols @@ -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)) + diff --git a/tests/baselines/reference/nonPrimitiveAndEmptyObject.types b/tests/baselines/reference/nonPrimitiveAndEmptyObject.types new file mode 100644 index 0000000000000..3fa7edd90982f --- /dev/null +++ b/tests/baselines/reference/nonPrimitiveAndEmptyObject.types @@ -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 + diff --git a/tests/cases/conformance/types/nonPrimitive/nonPrimitiveAndEmptyObject.ts b/tests/cases/conformance/types/nonPrimitive/nonPrimitiveAndEmptyObject.ts new file mode 100644 index 0000000000000..0b3750eafeb80 --- /dev/null +++ b/tests/cases/conformance/types/nonPrimitive/nonPrimitiveAndEmptyObject.ts @@ -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;