From 444fc8764e7feb8b77e320abd5eb36992061fbee Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 28 Sep 2025 10:50:23 -0400 Subject: [PATCH 1/2] Types with extra properties are never subtypes of fresh object literals --- internal/checker/relater.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/internal/checker/relater.go b/internal/checker/relater.go index e6315974d0..851549487c 100644 --- a/internal/checker/relater.go +++ b/internal/checker/relater.go @@ -4153,16 +4153,13 @@ func (r *Relater) propertiesRelatedTo(source *Type, target *Type, reportErrors b } return TernaryFalse } - if isObjectLiteralType(target) { + if target.objectFlags&ObjectFlagsFreshLiteral != 0 { for _, sourceProp := range excludeProperties(r.c.getPropertiesOfType(source), excludedProperties) { if r.c.getPropertyOfObjectType(target, sourceProp.Name) == nil { - sourceType := r.c.getTypeOfSymbol(sourceProp) - if sourceType.flags&TypeFlagsUndefined == 0 { - if reportErrors { - r.reportError(diagnostics.Property_0_does_not_exist_on_type_1, r.c.symbolToString(sourceProp), r.c.TypeToString(target)) - } - return TernaryFalse + if reportErrors { + r.reportError(diagnostics.Property_0_does_not_exist_on_type_1, r.c.symbolToString(sourceProp), r.c.TypeToString(target)) } + return TernaryFalse } } } From eeba5ca37703ea177a70945d212d706745f068fc Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 28 Sep 2025 10:50:47 -0400 Subject: [PATCH 2/2] Add tests --- .../freshObjectLiteralSubtype.symbols | 65 +++++++++++++++ .../compiler/freshObjectLiteralSubtype.types | 80 +++++++++++++++++++ .../compiler/freshObjectLiteralSubtype.ts | 33 ++++++++ 3 files changed, 178 insertions(+) create mode 100644 testdata/baselines/reference/compiler/freshObjectLiteralSubtype.symbols create mode 100644 testdata/baselines/reference/compiler/freshObjectLiteralSubtype.types create mode 100644 testdata/tests/cases/compiler/freshObjectLiteralSubtype.ts diff --git a/testdata/baselines/reference/compiler/freshObjectLiteralSubtype.symbols b/testdata/baselines/reference/compiler/freshObjectLiteralSubtype.symbols new file mode 100644 index 0000000000..33baa87105 --- /dev/null +++ b/testdata/baselines/reference/compiler/freshObjectLiteralSubtype.symbols @@ -0,0 +1,65 @@ +//// [tests/cases/compiler/freshObjectLiteralSubtype.ts] //// + +=== freshObjectLiteralSubtype.ts === +function f1() { +>f1 : Symbol(f1, Decl(freshObjectLiteralSubtype.ts, 0, 0)) + + if (!!true) { + return { valid: true } +>valid : Symbol(valid, Decl(freshObjectLiteralSubtype.ts, 2, 16)) + } + return f2() +>f2 : Symbol(f2, Decl(freshObjectLiteralSubtype.ts, 7, 13)) +} + +declare const f2: () => { valid: boolean, msg?: undefined } +>f2 : Symbol(f2, Decl(freshObjectLiteralSubtype.ts, 7, 13)) +>valid : Symbol(valid, Decl(freshObjectLiteralSubtype.ts, 7, 25)) +>msg : Symbol(msg, Decl(freshObjectLiteralSubtype.ts, 7, 41)) + +f1().msg +>f1().msg : Symbol(msg, Decl(freshObjectLiteralSubtype.ts, 7, 41)) +>f1 : Symbol(f1, Decl(freshObjectLiteralSubtype.ts, 0, 0)) +>msg : Symbol(msg, Decl(freshObjectLiteralSubtype.ts, 7, 41)) + +// Repro from https://github.com/microsoft/typescript-go/issues/1742 + +function validate() { +>validate : Symbol(validate, Decl(freshObjectLiteralSubtype.ts, 9, 8)) + + if(Math.random() > 0.5) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return utilValidate(); +>utilValidate : Symbol(utilValidate, Decl(freshObjectLiteralSubtype.ts, 18, 2)) + } + return { valid: true }; +>valid : Symbol(valid, Decl(freshObjectLiteralSubtype.ts, 17, 12)) + +}; + + +declare function utilValidate(): { +>utilValidate : Symbol(utilValidate, Decl(freshObjectLiteralSubtype.ts, 18, 2)) + + valid: boolean; +>valid : Symbol(valid, Decl(freshObjectLiteralSubtype.ts, 21, 34)) + + msg?: undefined; +>msg : Symbol(msg, Decl(freshObjectLiteralSubtype.ts, 22, 19)) + +} | { + valid: boolean; +>valid : Symbol(valid, Decl(freshObjectLiteralSubtype.ts, 24, 5)) + + msg: string; +>msg : Symbol(msg, Decl(freshObjectLiteralSubtype.ts, 25, 19)) +} + +validate().msg; // Error in TSGO +>validate().msg : Symbol(msg, Decl(freshObjectLiteralSubtype.ts, 22, 19), Decl(freshObjectLiteralSubtype.ts, 25, 19)) +>validate : Symbol(validate, Decl(freshObjectLiteralSubtype.ts, 9, 8)) +>msg : Symbol(msg, Decl(freshObjectLiteralSubtype.ts, 22, 19), Decl(freshObjectLiteralSubtype.ts, 25, 19)) + diff --git a/testdata/baselines/reference/compiler/freshObjectLiteralSubtype.types b/testdata/baselines/reference/compiler/freshObjectLiteralSubtype.types new file mode 100644 index 0000000000..67d163471e --- /dev/null +++ b/testdata/baselines/reference/compiler/freshObjectLiteralSubtype.types @@ -0,0 +1,80 @@ +//// [tests/cases/compiler/freshObjectLiteralSubtype.ts] //// + +=== freshObjectLiteralSubtype.ts === +function f1() { +>f1 : () => { valid: boolean; msg?: undefined; } + + if (!!true) { +>!!true : true +>!true : false +>true : true + + return { valid: true } +>{ valid: true } : { valid: boolean; } +>valid : boolean +>true : true + } + return f2() +>f2() : { valid: boolean; msg?: undefined; } +>f2 : () => { valid: boolean; msg?: undefined; } +} + +declare const f2: () => { valid: boolean, msg?: undefined } +>f2 : () => { valid: boolean; msg?: undefined; } +>valid : boolean +>msg : undefined + +f1().msg +>f1().msg : undefined +>f1() : { valid: boolean; msg?: undefined; } +>f1 : () => { valid: boolean; msg?: undefined; } +>msg : undefined + +// Repro from https://github.com/microsoft/typescript-go/issues/1742 + +function validate() { +>validate : () => { valid: boolean; msg?: undefined; } | { valid: boolean; msg: string; } + + if(Math.random() > 0.5) { +>Math.random() > 0.5 : boolean +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>0.5 : 0.5 + + return utilValidate(); +>utilValidate() : { valid: boolean; msg?: undefined; } | { valid: boolean; msg: string; } +>utilValidate : () => { valid: boolean; msg?: undefined; } | { valid: boolean; msg: string; } + } + return { valid: true }; +>{ valid: true } : { valid: boolean; } +>valid : boolean +>true : true + +}; + + +declare function utilValidate(): { +>utilValidate : () => { valid: boolean; msg?: undefined; } | { valid: boolean; msg: string; } + + valid: boolean; +>valid : boolean + + msg?: undefined; +>msg : undefined + +} | { + valid: boolean; +>valid : boolean + + msg: string; +>msg : string +} + +validate().msg; // Error in TSGO +>validate().msg : string | undefined +>validate() : { valid: boolean; msg?: undefined; } | { valid: boolean; msg: string; } +>validate : () => { valid: boolean; msg?: undefined; } | { valid: boolean; msg: string; } +>msg : string | undefined + diff --git a/testdata/tests/cases/compiler/freshObjectLiteralSubtype.ts b/testdata/tests/cases/compiler/freshObjectLiteralSubtype.ts new file mode 100644 index 0000000000..750d818586 --- /dev/null +++ b/testdata/tests/cases/compiler/freshObjectLiteralSubtype.ts @@ -0,0 +1,33 @@ +// @strict: true +// @noEmit: true + +function f1() { + if (!!true) { + return { valid: true } + } + return f2() +} + +declare const f2: () => { valid: boolean, msg?: undefined } + +f1().msg + +// Repro from https://github.com/microsoft/typescript-go/issues/1742 + +function validate() { + if(Math.random() > 0.5) { + return utilValidate(); + } + return { valid: true }; +}; + + +declare function utilValidate(): { + valid: boolean; + msg?: undefined; +} | { + valid: boolean; + msg: string; +} + +validate().msg; // Error in TSGO