From 97a061dd2ee66d1b110bce090b2bac6550c4ce89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 5 Nov 2025 18:51:28 +0100 Subject: [PATCH 1/2] Widen reverse mapped type properties to fix them being treated as EPC-valid sources --- src/compiler/checker.ts | 2 +- ...everseMappedTypeInferenceWidening1.symbols | 181 +++++++++++++ .../reverseMappedTypeInferenceWidening1.types | 244 ++++++++++++++++++ .../reverseMappedTypeInferenceWidening1.ts | 60 +++++ 4 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/reverseMappedTypeInferenceWidening1.symbols create mode 100644 tests/baselines/reference/reverseMappedTypeInferenceWidening1.types create mode 100644 tests/cases/compiler/reverseMappedTypeInferenceWidening1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 480f7189fce31..f84b6cf5cb67f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26421,7 +26421,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { - links.type = inferReverseMappedType(symbol.links.propertyType, symbol.links.mappedType, symbol.links.constraintType) || unknownType; + links.type = getWidenedType(inferReverseMappedType(symbol.links.propertyType, symbol.links.mappedType, symbol.links.constraintType) || unknownType); } return links.type; } diff --git a/tests/baselines/reference/reverseMappedTypeInferenceWidening1.symbols b/tests/baselines/reference/reverseMappedTypeInferenceWidening1.symbols new file mode 100644 index 0000000000000..635dcd0afed7d --- /dev/null +++ b/tests/baselines/reference/reverseMappedTypeInferenceWidening1.symbols @@ -0,0 +1,181 @@ +//// [tests/cases/compiler/reverseMappedTypeInferenceWidening1.ts] //// + +=== reverseMappedTypeInferenceWidening1.ts === +// https://github.com/microsoft/TypeScript/issues/62720 + +type TypeFunction = (...args: any[]) => ReturnType; +>TypeFunction : Symbol(TypeFunction, Decl(reverseMappedTypeInferenceWidening1.ts, 0, 0)) +>ReturnType : Symbol(ReturnType, Decl(reverseMappedTypeInferenceWidening1.ts, 2, 18)) +>args : Symbol(args, Decl(reverseMappedTypeInferenceWidening1.ts, 2, 43)) +>ReturnType : Symbol(ReturnType, Decl(reverseMappedTypeInferenceWidening1.ts, 2, 18)) + +type Flags = { +>Flags : Symbol(Flags, Decl(reverseMappedTypeInferenceWidening1.ts, 2, 73)) + + [flagName: string]: { +>flagName : Symbol(flagName, Decl(reverseMappedTypeInferenceWidening1.ts, 4, 3)) + + type: TypeFunction; +>type : Symbol(type, Decl(reverseMappedTypeInferenceWidening1.ts, 4, 23)) +>TypeFunction : Symbol(TypeFunction, Decl(reverseMappedTypeInferenceWidening1.ts, 0, 0)) + + default?: unknown; +>default : Symbol(default, Decl(reverseMappedTypeInferenceWidening1.ts, 5, 23)) + + }; +}; +type TypeFlag = { +>TypeFlag : Symbol(TypeFlag, Decl(reverseMappedTypeInferenceWidening1.ts, 8, 2)) +>Schemas : Symbol(Schemas, Decl(reverseMappedTypeInferenceWidening1.ts, 9, 14)) +>Flags : Symbol(Flags, Decl(reverseMappedTypeInferenceWidening1.ts, 2, 73)) + + [flag in keyof Schemas]: Schemas[flag] extends { type: TypeFunction } +>flag : Symbol(flag, Decl(reverseMappedTypeInferenceWidening1.ts, 10, 3)) +>Schemas : Symbol(Schemas, Decl(reverseMappedTypeInferenceWidening1.ts, 9, 14)) +>Schemas : Symbol(Schemas, Decl(reverseMappedTypeInferenceWidening1.ts, 9, 14)) +>flag : Symbol(flag, Decl(reverseMappedTypeInferenceWidening1.ts, 10, 3)) +>type : Symbol(type, Decl(reverseMappedTypeInferenceWidening1.ts, 10, 50)) +>TypeFunction : Symbol(TypeFunction, Decl(reverseMappedTypeInferenceWidening1.ts, 0, 0)) +>T : Symbol(T, Decl(reverseMappedTypeInferenceWidening1.ts, 10, 75)) + + ? T +>T : Symbol(T, Decl(reverseMappedTypeInferenceWidening1.ts, 10, 75)) + + : never; +}; + +declare function fn1( +>fn1 : Symbol(fn1, Decl(reverseMappedTypeInferenceWidening1.ts, 13, 2)) +>Options : Symbol(Options, Decl(reverseMappedTypeInferenceWidening1.ts, 15, 21)) +>Flags : Symbol(Flags, Decl(reverseMappedTypeInferenceWidening1.ts, 2, 73)) + + options: Options, +>options : Symbol(options, Decl(reverseMappedTypeInferenceWidening1.ts, 15, 44)) +>Options : Symbol(Options, Decl(reverseMappedTypeInferenceWidening1.ts, 15, 21)) + +): TypeFlag; +>TypeFlag : Symbol(TypeFlag, Decl(reverseMappedTypeInferenceWidening1.ts, 8, 2)) +>Options : Symbol(Options, Decl(reverseMappedTypeInferenceWidening1.ts, 15, 21)) + +const result1 = fn1({ +>result1 : Symbol(result1, Decl(reverseMappedTypeInferenceWidening1.ts, 19, 5)) +>fn1 : Symbol(fn1, Decl(reverseMappedTypeInferenceWidening1.ts, 13, 2)) + + booleanFlag: { type: Boolean }, +>booleanFlag : Symbol(booleanFlag, Decl(reverseMappedTypeInferenceWidening1.ts, 19, 21)) +>type : Symbol(type, Decl(reverseMappedTypeInferenceWidening1.ts, 20, 16)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + booleanFlagDefault: { +>booleanFlagDefault : Symbol(booleanFlagDefault, Decl(reverseMappedTypeInferenceWidening1.ts, 20, 33)) + + type: Boolean, +>type : Symbol(type, Decl(reverseMappedTypeInferenceWidening1.ts, 21, 23)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + default: false, +>default : Symbol(default, Decl(reverseMappedTypeInferenceWidening1.ts, 22, 18)) + + }, +}); +result1.booleanFlag; // boolean +>result1.booleanFlag : Symbol(booleanFlag, Decl(reverseMappedTypeInferenceWidening1.ts, 19, 21)) +>result1 : Symbol(result1, Decl(reverseMappedTypeInferenceWidening1.ts, 19, 5)) +>booleanFlag : Symbol(booleanFlag, Decl(reverseMappedTypeInferenceWidening1.ts, 19, 21)) + +result1.booleanFlagDefault; // boolean +>result1.booleanFlagDefault : Symbol(booleanFlagDefault, Decl(reverseMappedTypeInferenceWidening1.ts, 20, 33)) +>result1 : Symbol(result1, Decl(reverseMappedTypeInferenceWidening1.ts, 19, 5)) +>booleanFlagDefault : Symbol(booleanFlagDefault, Decl(reverseMappedTypeInferenceWidening1.ts, 20, 33)) + +declare function fn2( +>fn2 : Symbol(fn2, Decl(reverseMappedTypeInferenceWidening1.ts, 27, 27)) +>Options : Symbol(Options, Decl(reverseMappedTypeInferenceWidening1.ts, 29, 21)) +>Flags : Symbol(Flags, Decl(reverseMappedTypeInferenceWidening1.ts, 2, 73)) + + options: Readonly, +>options : Symbol(options, Decl(reverseMappedTypeInferenceWidening1.ts, 29, 44)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>Options : Symbol(Options, Decl(reverseMappedTypeInferenceWidening1.ts, 29, 21)) + +): TypeFlag; +>TypeFlag : Symbol(TypeFlag, Decl(reverseMappedTypeInferenceWidening1.ts, 8, 2)) +>Options : Symbol(Options, Decl(reverseMappedTypeInferenceWidening1.ts, 29, 21)) + +const result2 = fn2({ +>result2 : Symbol(result2, Decl(reverseMappedTypeInferenceWidening1.ts, 33, 5)) +>fn2 : Symbol(fn2, Decl(reverseMappedTypeInferenceWidening1.ts, 27, 27)) + + booleanFlag: { type: Boolean }, +>booleanFlag : Symbol(booleanFlag, Decl(reverseMappedTypeInferenceWidening1.ts, 33, 21)) +>type : Symbol(type, Decl(reverseMappedTypeInferenceWidening1.ts, 34, 16)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + booleanFlagDefault: { +>booleanFlagDefault : Symbol(booleanFlagDefault, Decl(reverseMappedTypeInferenceWidening1.ts, 34, 33)) + + type: Boolean, +>type : Symbol(type, Decl(reverseMappedTypeInferenceWidening1.ts, 35, 23)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + default: false, +>default : Symbol(default, Decl(reverseMappedTypeInferenceWidening1.ts, 36, 18)) + + }, +}); +result2.booleanFlag; // boolean +>result2.booleanFlag : Symbol(booleanFlag, Decl(reverseMappedTypeInferenceWidening1.ts, 33, 21)) +>result2 : Symbol(result2, Decl(reverseMappedTypeInferenceWidening1.ts, 33, 5)) +>booleanFlag : Symbol(booleanFlag, Decl(reverseMappedTypeInferenceWidening1.ts, 33, 21)) + +result2.booleanFlagDefault; // boolean +>result2.booleanFlagDefault : Symbol(booleanFlagDefault, Decl(reverseMappedTypeInferenceWidening1.ts, 34, 33)) +>result2 : Symbol(result2, Decl(reverseMappedTypeInferenceWidening1.ts, 33, 5)) +>booleanFlagDefault : Symbol(booleanFlagDefault, Decl(reverseMappedTypeInferenceWidening1.ts, 34, 33)) + +declare function fn3( +>fn3 : Symbol(fn3, Decl(reverseMappedTypeInferenceWidening1.ts, 41, 27)) +>Options : Symbol(Options, Decl(reverseMappedTypeInferenceWidening1.ts, 43, 21)) +>Flags : Symbol(Flags, Decl(reverseMappedTypeInferenceWidening1.ts, 2, 73)) + + options: Readonly, +>options : Symbol(options, Decl(reverseMappedTypeInferenceWidening1.ts, 43, 44)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>Options : Symbol(Options, Decl(reverseMappedTypeInferenceWidening1.ts, 43, 21)) + +): Options; +>Options : Symbol(Options, Decl(reverseMappedTypeInferenceWidening1.ts, 43, 21)) + +const result3 = fn3({ +>result3 : Symbol(result3, Decl(reverseMappedTypeInferenceWidening1.ts, 47, 5)) +>fn3 : Symbol(fn3, Decl(reverseMappedTypeInferenceWidening1.ts, 41, 27)) + + booleanFlag: { type: Boolean }, +>booleanFlag : Symbol(booleanFlag, Decl(reverseMappedTypeInferenceWidening1.ts, 47, 21)) +>type : Symbol(type, Decl(reverseMappedTypeInferenceWidening1.ts, 48, 16)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + booleanFlagDefault: { +>booleanFlagDefault : Symbol(booleanFlagDefault, Decl(reverseMappedTypeInferenceWidening1.ts, 48, 33)) + + type: Boolean, +>type : Symbol(type, Decl(reverseMappedTypeInferenceWidening1.ts, 49, 23)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + default: false, // no cursed EPC failure here +>default : Symbol(default, Decl(reverseMappedTypeInferenceWidening1.ts, 50, 18)) + + }, +}); + +declare function takeType(arg: { type: unknown }): void; +>takeType : Symbol(takeType, Decl(reverseMappedTypeInferenceWidening1.ts, 53, 3)) +>arg : Symbol(arg, Decl(reverseMappedTypeInferenceWidening1.ts, 55, 26)) +>type : Symbol(type, Decl(reverseMappedTypeInferenceWidening1.ts, 55, 32)) + +takeType(result3.booleanFlagDefault); +>takeType : Symbol(takeType, Decl(reverseMappedTypeInferenceWidening1.ts, 53, 3)) +>result3.booleanFlagDefault : Symbol(booleanFlagDefault, Decl(reverseMappedTypeInferenceWidening1.ts, 48, 33)) +>result3 : Symbol(result3, Decl(reverseMappedTypeInferenceWidening1.ts, 47, 5)) +>booleanFlagDefault : Symbol(booleanFlagDefault, Decl(reverseMappedTypeInferenceWidening1.ts, 48, 33)) + diff --git a/tests/baselines/reference/reverseMappedTypeInferenceWidening1.types b/tests/baselines/reference/reverseMappedTypeInferenceWidening1.types new file mode 100644 index 0000000000000..f8001f76ebe68 --- /dev/null +++ b/tests/baselines/reference/reverseMappedTypeInferenceWidening1.types @@ -0,0 +1,244 @@ +//// [tests/cases/compiler/reverseMappedTypeInferenceWidening1.ts] //// + +=== reverseMappedTypeInferenceWidening1.ts === +// https://github.com/microsoft/TypeScript/issues/62720 + +type TypeFunction = (...args: any[]) => ReturnType; +>TypeFunction : TypeFunction +> : ^^^^^^^^^^^^^^^^^^^^^^^^ +>args : any[] +> : ^^^^^ + +type Flags = { +>Flags : Flags +> : ^^^^^ + + [flagName: string]: { +>flagName : string +> : ^^^^^^ + + type: TypeFunction; +>type : TypeFunction +> : ^^^^^^^^^^^^^^^^^^^^^ + + default?: unknown; +>default : unknown +> : ^^^^^^^ + + }; +}; +type TypeFlag = { +>TypeFlag : TypeFlag +> : ^^^^^^^^^^^^^^^^^ + + [flag in keyof Schemas]: Schemas[flag] extends { type: TypeFunction } +>type : TypeFunction +> : ^^^^^^^^^^^^^^^ + + ? T + : never; +}; + +declare function fn1( +>fn1 : (options: Options) => TypeFlag +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ + + options: Options, +>options : Options +> : ^^^^^^^ + +): TypeFlag; + +const result1 = fn1({ +>result1 : TypeFlag<{ booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: boolean; }; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>fn1({ booleanFlag: { type: Boolean }, booleanFlagDefault: { type: Boolean, default: false, },}) : TypeFlag<{ booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: boolean; }; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>fn1 : (options: Options) => TypeFlag +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ booleanFlag: { type: Boolean }, booleanFlagDefault: { type: Boolean, default: false, },} : { booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: false; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + booleanFlag: { type: Boolean }, +>booleanFlag : { type: BooleanConstructor; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ type: Boolean } : { type: BooleanConstructor; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ +>Boolean : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ + + booleanFlagDefault: { +>booleanFlagDefault : { type: BooleanConstructor; default: false; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ type: Boolean, default: false, } : { type: BooleanConstructor; default: false; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + type: Boolean, +>type : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ +>Boolean : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ + + default: false, +>default : false +> : ^^^^^ +>false : false +> : ^^^^^ + + }, +}); +result1.booleanFlag; // boolean +>result1.booleanFlag : boolean +> : ^^^^^^^ +>result1 : TypeFlag<{ booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: boolean; }; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>booleanFlag : boolean +> : ^^^^^^^ + +result1.booleanFlagDefault; // boolean +>result1.booleanFlagDefault : boolean +> : ^^^^^^^ +>result1 : TypeFlag<{ booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: boolean; }; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>booleanFlagDefault : boolean +> : ^^^^^^^ + +declare function fn2( +>fn2 : (options: Readonly) => TypeFlag +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ + + options: Readonly, +>options : Readonly +> : ^^^^^^^^^^^^^^^^^ + +): TypeFlag; + +const result2 = fn2({ +>result2 : TypeFlag<{ booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: boolean; }; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>fn2({ booleanFlag: { type: Boolean }, booleanFlagDefault: { type: Boolean, default: false, },}) : TypeFlag<{ booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: boolean; }; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>fn2 : (options: Readonly) => TypeFlag +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ booleanFlag: { type: Boolean }, booleanFlagDefault: { type: Boolean, default: false, },} : { booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: false; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + booleanFlag: { type: Boolean }, +>booleanFlag : { type: BooleanConstructor; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ type: Boolean } : { type: BooleanConstructor; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ +>Boolean : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ + + booleanFlagDefault: { +>booleanFlagDefault : { type: BooleanConstructor; default: false; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ type: Boolean, default: false, } : { type: BooleanConstructor; default: false; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + type: Boolean, +>type : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ +>Boolean : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ + + default: false, +>default : false +> : ^^^^^ +>false : false +> : ^^^^^ + + }, +}); +result2.booleanFlag; // boolean +>result2.booleanFlag : boolean +> : ^^^^^^^ +>result2 : TypeFlag<{ booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: boolean; }; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>booleanFlag : boolean +> : ^^^^^^^ + +result2.booleanFlagDefault; // boolean +>result2.booleanFlagDefault : boolean +> : ^^^^^^^ +>result2 : TypeFlag<{ booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: boolean; }; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>booleanFlagDefault : boolean +> : ^^^^^^^ + +declare function fn3( +>fn3 : (options: Readonly) => Options +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ + + options: Readonly, +>options : Readonly +> : ^^^^^^^^^^^^^^^^^ + +): Options; + +const result3 = fn3({ +>result3 : { booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>fn3({ booleanFlag: { type: Boolean }, booleanFlagDefault: { type: Boolean, default: false, // no cursed EPC failure here },}) : { booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>fn3 : (options: Readonly) => Options +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ booleanFlag: { type: Boolean }, booleanFlagDefault: { type: Boolean, default: false, // no cursed EPC failure here },} : { booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: false; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + booleanFlag: { type: Boolean }, +>booleanFlag : { type: BooleanConstructor; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ type: Boolean } : { type: BooleanConstructor; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>type : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ +>Boolean : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ + + booleanFlagDefault: { +>booleanFlagDefault : { type: BooleanConstructor; default: false; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ type: Boolean, default: false, // no cursed EPC failure here } : { type: BooleanConstructor; default: false; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + type: Boolean, +>type : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ +>Boolean : BooleanConstructor +> : ^^^^^^^^^^^^^^^^^^ + + default: false, // no cursed EPC failure here +>default : false +> : ^^^^^ +>false : false +> : ^^^^^ + + }, +}); + +declare function takeType(arg: { type: unknown }): void; +>takeType : (arg: { type: unknown; }) => void +> : ^ ^^ ^^^^^ +>arg : { type: unknown; } +> : ^^^^^^^^ ^^^ +>type : unknown +> : ^^^^^^^ + +takeType(result3.booleanFlagDefault); +>takeType(result3.booleanFlagDefault) : void +> : ^^^^ +>takeType : (arg: { type: unknown; }) => void +> : ^ ^^ ^^^^^ +>result3.booleanFlagDefault : { type: BooleanConstructor; default: boolean; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>result3 : { booleanFlag: { type: BooleanConstructor; }; booleanFlagDefault: { type: BooleanConstructor; default: boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>booleanFlagDefault : { type: BooleanConstructor; default: boolean; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/cases/compiler/reverseMappedTypeInferenceWidening1.ts b/tests/cases/compiler/reverseMappedTypeInferenceWidening1.ts new file mode 100644 index 0000000000000..bbaae1d00e01e --- /dev/null +++ b/tests/cases/compiler/reverseMappedTypeInferenceWidening1.ts @@ -0,0 +1,60 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/62720 + +type TypeFunction = (...args: any[]) => ReturnType; +type Flags = { + [flagName: string]: { + type: TypeFunction; + default?: unknown; + }; +}; +type TypeFlag = { + [flag in keyof Schemas]: Schemas[flag] extends { type: TypeFunction } + ? T + : never; +}; + +declare function fn1( + options: Options, +): TypeFlag; + +const result1 = fn1({ + booleanFlag: { type: Boolean }, + booleanFlagDefault: { + type: Boolean, + default: false, + }, +}); +result1.booleanFlag; // boolean +result1.booleanFlagDefault; // boolean + +declare function fn2( + options: Readonly, +): TypeFlag; + +const result2 = fn2({ + booleanFlag: { type: Boolean }, + booleanFlagDefault: { + type: Boolean, + default: false, + }, +}); +result2.booleanFlag; // boolean +result2.booleanFlagDefault; // boolean + +declare function fn3( + options: Readonly, +): Options; + +const result3 = fn3({ + booleanFlag: { type: Boolean }, + booleanFlagDefault: { + type: Boolean, + default: false, // no cursed EPC failure here + }, +}); + +declare function takeType(arg: { type: unknown }): void; +takeType(result3.booleanFlagDefault); From 6fd5860b0aea561e8f25ab148962ac846db1eb19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 5 Nov 2025 18:58:22 +0100 Subject: [PATCH 2/2] move the fix to `inferReverseMappedTypeWorker` --- 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 f84b6cf5cb67f..410b2c8913f18 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26421,7 +26421,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { - links.type = getWidenedType(inferReverseMappedType(symbol.links.propertyType, symbol.links.mappedType, symbol.links.constraintType) || unknownType); + links.type = inferReverseMappedType(symbol.links.propertyType, symbol.links.mappedType, symbol.links.constraintType) || unknownType; } return links.type; } @@ -26431,7 +26431,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const templateType = getTemplateTypeFromMappedType(target); const inference = createInferenceInfo(typeParameter); inferTypes([inference], sourceType, templateType); - return getTypeFromInference(inference) || unknownType; + return getWidenedType(getTypeFromInference(inference) || unknownType); } function inferReverseMappedType(source: Type, target: MappedType, constraint: IndexType): Type | undefined {