From d28d0c2c3adecc3a76345c621d5f193b1dbccbab Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Aug 2025 07:24:44 -0700 Subject: [PATCH 1/2] Prefer implicit {} over explicitly declared {} in subtype reduction --- internal/checker/checker.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 3c216fc6f8..93b68e4f04 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -25010,6 +25010,9 @@ func (c *Checker) removeSubtypes(types []*Type, hasObjectTypes bool) []*Type { continue } } + if (source == c.emptyObjectType || source == c.unknownEmptyObjectType) && target.symbol != nil && c.IsEmptyAnonymousObjectType(target) { + continue + } if c.isTypeRelatedTo(source, target, c.strictSubtypeRelation) && (c.getTargetType(source).objectFlags&ObjectFlagsClass == 0 || c.getTargetType(target).objectFlags&ObjectFlagsClass == 0 || c.isTypeDerivedFrom(source, target)) { From 83a326a173e962ddd61711bf8740e7fbb6355d52 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Aug 2025 07:28:56 -0700 Subject: [PATCH 2/2] Add regression test --- .../implicitEmptyObjectType.errors.txt | 16 ++++++++++++ .../compiler/implicitEmptyObjectType.symbols | 21 ++++++++++++++++ .../compiler/implicitEmptyObjectType.types | 25 +++++++++++++++++++ .../cases/compiler/implicitEmptyObjectType.ts | 10 ++++++++ 4 files changed, 72 insertions(+) create mode 100644 testdata/baselines/reference/compiler/implicitEmptyObjectType.errors.txt create mode 100644 testdata/baselines/reference/compiler/implicitEmptyObjectType.symbols create mode 100644 testdata/baselines/reference/compiler/implicitEmptyObjectType.types create mode 100644 testdata/tests/cases/compiler/implicitEmptyObjectType.ts diff --git a/testdata/baselines/reference/compiler/implicitEmptyObjectType.errors.txt b/testdata/baselines/reference/compiler/implicitEmptyObjectType.errors.txt new file mode 100644 index 0000000000..8470239c49 --- /dev/null +++ b/testdata/baselines/reference/compiler/implicitEmptyObjectType.errors.txt @@ -0,0 +1,16 @@ +implicitEmptyObjectType.ts(6,17): error TS2345: Argument of type '{}' is not assignable to parameter of type 'Record'. + Index signature for type 'string' is missing in type '{}'. + + +==== implicitEmptyObjectType.ts (1 errors) ==== + // https://github.com/microsoft/typescript-go/issues/1563 + + function f() { + const v: unknown = "lol"; + const acceptsRecord = (record: Record) => {}; + acceptsRecord(v || {}); + ~~~~~~~ +!!! error TS2345: Argument of type '{}' is not assignable to parameter of type 'Record'. +!!! error TS2345: Index signature for type 'string' is missing in type '{}'. + } + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/implicitEmptyObjectType.symbols b/testdata/baselines/reference/compiler/implicitEmptyObjectType.symbols new file mode 100644 index 0000000000..af91d68f3c --- /dev/null +++ b/testdata/baselines/reference/compiler/implicitEmptyObjectType.symbols @@ -0,0 +1,21 @@ +//// [tests/cases/compiler/implicitEmptyObjectType.ts] //// + +=== implicitEmptyObjectType.ts === +// https://github.com/microsoft/typescript-go/issues/1563 + +function f() { +>f : Symbol(f, Decl(implicitEmptyObjectType.ts, 0, 0)) + + const v: unknown = "lol"; +>v : Symbol(v, Decl(implicitEmptyObjectType.ts, 3, 7)) + + const acceptsRecord = (record: Record) => {}; +>acceptsRecord : Symbol(acceptsRecord, Decl(implicitEmptyObjectType.ts, 4, 7)) +>record : Symbol(record, Decl(implicitEmptyObjectType.ts, 4, 25)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + + acceptsRecord(v || {}); +>acceptsRecord : Symbol(acceptsRecord, Decl(implicitEmptyObjectType.ts, 4, 7)) +>v : Symbol(v, Decl(implicitEmptyObjectType.ts, 3, 7)) +} + diff --git a/testdata/baselines/reference/compiler/implicitEmptyObjectType.types b/testdata/baselines/reference/compiler/implicitEmptyObjectType.types new file mode 100644 index 0000000000..7721a68884 --- /dev/null +++ b/testdata/baselines/reference/compiler/implicitEmptyObjectType.types @@ -0,0 +1,25 @@ +//// [tests/cases/compiler/implicitEmptyObjectType.ts] //// + +=== implicitEmptyObjectType.ts === +// https://github.com/microsoft/typescript-go/issues/1563 + +function f() { +>f : () => void + + const v: unknown = "lol"; +>v : unknown +>"lol" : "lol" + + const acceptsRecord = (record: Record) => {}; +>acceptsRecord : (record: Record) => void +>(record: Record) => {} : (record: Record) => void +>record : Record + + acceptsRecord(v || {}); +>acceptsRecord(v || {}) : void +>acceptsRecord : (record: Record) => void +>v || {} : {} +>v : unknown +>{} : {} +} + diff --git a/testdata/tests/cases/compiler/implicitEmptyObjectType.ts b/testdata/tests/cases/compiler/implicitEmptyObjectType.ts new file mode 100644 index 0000000000..746d633ded --- /dev/null +++ b/testdata/tests/cases/compiler/implicitEmptyObjectType.ts @@ -0,0 +1,10 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/typescript-go/issues/1563 + +function f() { + const v: unknown = "lol"; + const acceptsRecord = (record: Record) => {}; + acceptsRecord(v || {}); +}