From 09e5824267a72c21bfa9469504f45800506bddd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 20 Jul 2025 08:51:12 +0200 Subject: [PATCH 1/6] Combine inferences from distributive conditional types --- src/compiler/checker.ts | 15 +- src/compiler/types.ts | 10 +- tests/baselines/reference/api/typescript.d.ts | 9 +- ...FunctionParametersConditionalType1.symbols | 145 ++++++++++++++++++ ...icFunctionParametersConditionalType1.types | 131 ++++++++++++++++ .../typePredicateFreshLiteralWidening.symbols | 4 +- .../typePredicateFreshLiteralWidening.types | 100 ++++++------ ...nericFunctionParametersConditionalType1.ts | 51 ++++++ 8 files changed, 402 insertions(+), 63 deletions(-) create mode 100644 tests/baselines/reference/genericFunctionParametersConditionalType1.symbols create mode 100644 tests/baselines/reference/genericFunctionParametersConditionalType1.types create mode 100644 tests/cases/compiler/genericFunctionParametersConditionalType1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c5701087ebfd4..30ec6c749bca1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26784,13 +26784,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (candidate === blockedStringType) { return; } - if (inference.priority === undefined || priority < inference.priority) { + const combinedPriority = priority | (inference.individualPriority || InferencePriority.None) + if (inference.priority === undefined || combinedPriority < inference.priority) { inference.candidates = undefined; inference.contraCandidates = undefined; inference.topLevel = true; - inference.priority = priority; + inference.priority = combinedPriority; } - if (priority === inference.priority) { + if (combinedPriority === inference.priority) { // We make contravariant inferences only if we are in a pure contravariant position, // i.e. only if we have not descended into a bivariant position. if (contravariant && !bivariant) { @@ -27156,6 +27157,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function inferToConditionalType(source: Type, target: ConditionalType) { + const info = target.root.isDistributive ? getInferenceInfoForType(getActualTypeVariable(target.checkType)) : undefined; + const saveIndividualPriority = info?.individualPriority + if (info) { + info.individualPriority = (info.individualPriority || InferencePriority.None) | InferencePriority.DistributiveConditional; + } if (source.flags & TypeFlags.Conditional) { inferFromTypes((source as ConditionalType).checkType, target.checkType); inferFromTypes((source as ConditionalType).extendsType, target.extendsType); @@ -27166,6 +27172,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const targetTypes = [getTrueTypeFromConditionalType(target), getFalseTypeFromConditionalType(target)]; inferToMultipleTypesWithPriority(source, targetTypes, target.flags, contravariant ? InferencePriority.ContravariantConditional : 0); } + if (info) { + info.individualPriority = saveIndividualPriority; + } } function inferToTemplateLiteralType(source: Type, target: TemplateLiteralType) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1cfe3e04ba68d..8bbfa50a90c84 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7102,11 +7102,12 @@ export const enum InferencePriority { ContravariantConditional = 1 << 6, // Conditional type in contravariant position ReturnType = 1 << 7, // Inference made from return type of generic function LiteralKeyof = 1 << 8, // Inference made from a string literal to a keyof T - NoConstraints = 1 << 9, // Don't infer from constraints of instantiable types - AlwaysStrict = 1 << 10, // Always use strict rules for contravariant inferences - MaxValue = 1 << 11, // Seed for inference priority tracking + DistributiveConditional = 1 << 9, + NoConstraints = 1 << 10, // Don't infer from constraints of instantiable types + AlwaysStrict = 1 << 11, // Always use strict rules for contravariant inferences + MaxValue = 1 << 12, // Seed for inference priority tracking - PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates + PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof | DistributiveConditional, // These priorities imply that the resulting type should be a combination of all candidates Circularity = -1, // Inference circularity (value less than all other priorities) } @@ -7118,6 +7119,7 @@ export interface InferenceInfo { contraCandidates: Type[] | undefined; // Candidates in contravariant positions (or undefined) inferredType?: Type; // Cache for resolved inferred type priority?: InferencePriority; // Priority of current inference set + individualPriority?: InferencePriority; topLevel: boolean; // True if all inferences are to top level occurrences isFixed: boolean; // True if inferences are fixed impliedArity?: number; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 87942520f2ab0..752b0f6725da3 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -6893,10 +6893,11 @@ declare namespace ts { ContravariantConditional = 64, ReturnType = 128, LiteralKeyof = 256, - NoConstraints = 512, - AlwaysStrict = 1024, - MaxValue = 2048, - PriorityImpliesCombination = 416, + DistributiveConditional = 512, + NoConstraints = 1024, + AlwaysStrict = 2048, + MaxValue = 4096, + PriorityImpliesCombination = 928, Circularity = -1, } interface FileExtensionInfo { diff --git a/tests/baselines/reference/genericFunctionParametersConditionalType1.symbols b/tests/baselines/reference/genericFunctionParametersConditionalType1.symbols new file mode 100644 index 0000000000000..f89e37c677e74 --- /dev/null +++ b/tests/baselines/reference/genericFunctionParametersConditionalType1.symbols @@ -0,0 +1,145 @@ +//// [tests/cases/compiler/genericFunctionParametersConditionalType1.ts] //// + +=== genericFunctionParametersConditionalType1.ts === +// https://github.com/microsoft/TypeScript/issues/62079 + +export {}; + +interface _Map { +>_Map : Symbol(_Map, Decl(genericFunctionParametersConditionalType1.ts, 2, 10)) + + foo: { a: 123 } +>foo : Symbol(_Map.foo, Decl(genericFunctionParametersConditionalType1.ts, 4, 16)) +>a : Symbol(a, Decl(genericFunctionParametersConditionalType1.ts, 5, 8)) +} + +type ModuleSubType = "bar" & { brand: true }; +>ModuleSubType : Symbol(ModuleSubType, Decl(genericFunctionParametersConditionalType1.ts, 6, 1)) +>brand : Symbol(brand, Decl(genericFunctionParametersConditionalType1.ts, 8, 30)) + +type Map = _Map & Record +>Map : Symbol(Map, Decl(genericFunctionParametersConditionalType1.ts, 8, 45)) +>_Map : Symbol(_Map, Decl(genericFunctionParametersConditionalType1.ts, 2, 10)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>ModuleSubType : Symbol(ModuleSubType, Decl(genericFunctionParametersConditionalType1.ts, 6, 1)) +>blah : Symbol(blah, Decl(genericFunctionParametersConditionalType1.ts, 10, 41)) + +type SubTypeGet< +>SubTypeGet : Symbol(SubTypeGet, Decl(genericFunctionParametersConditionalType1.ts, 10, 57)) + + SubType extends string, +>SubType : Symbol(SubType, Decl(genericFunctionParametersConditionalType1.ts, 12, 16)) + + Map extends Record, +>Map : Symbol(Map, Decl(genericFunctionParametersConditionalType1.ts, 13, 25)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>SubType : Symbol(SubType, Decl(genericFunctionParametersConditionalType1.ts, 12, 16)) + +> = SubType extends unknown +>SubType : Symbol(SubType, Decl(genericFunctionParametersConditionalType1.ts, 12, 16)) + +? { type?: SubType } & Map[SubType] +>type : Symbol(type, Decl(genericFunctionParametersConditionalType1.ts, 16, 3)) +>SubType : Symbol(SubType, Decl(genericFunctionParametersConditionalType1.ts, 12, 16)) +>Map : Symbol(Map, Decl(genericFunctionParametersConditionalType1.ts, 13, 25)) +>SubType : Symbol(SubType, Decl(genericFunctionParametersConditionalType1.ts, 12, 16)) + +: never; + +type TestParameters = Parameters<(arg: SubTypeGet) => void> +>TestParameters : Symbol(TestParameters, Decl(genericFunctionParametersConditionalType1.ts, 17, 8)) +>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 19, 34)) +>ModuleSubType : Symbol(ModuleSubType, Decl(genericFunctionParametersConditionalType1.ts, 6, 1)) +>arg : Symbol(arg, Decl(genericFunctionParametersConditionalType1.ts, 19, 67)) +>SubTypeGet : Symbol(SubTypeGet, Decl(genericFunctionParametersConditionalType1.ts, 10, 57)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 19, 34)) +>Map : Symbol(Map, Decl(genericFunctionParametersConditionalType1.ts, 8, 45)) + +declare class Test { +>Test : Symbol(Test, Decl(genericFunctionParametersConditionalType1.ts, 19, 100)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 21, 19)) +>ModuleSubType : Symbol(ModuleSubType, Decl(genericFunctionParametersConditionalType1.ts, 6, 1)) + + constructor(arg: SubTypeGet); +>arg : Symbol(arg, Decl(genericFunctionParametersConditionalType1.ts, 22, 14)) +>SubTypeGet : Symbol(SubTypeGet, Decl(genericFunctionParametersConditionalType1.ts, 10, 57)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 21, 19)) +>Map : Symbol(Map, Decl(genericFunctionParametersConditionalType1.ts, 8, 45)) +} + +type TestConstructorParameters = ConstructorParameters; +>TestConstructorParameters : Symbol(TestConstructorParameters, Decl(genericFunctionParametersConditionalType1.ts, 23, 1)) +>ConstructorParameters : Symbol(ConstructorParameters, Decl(lib.es5.d.ts, --, --)) +>Test : Symbol(Test, Decl(genericFunctionParametersConditionalType1.ts, 19, 100)) + +declare class Animal { eat(): void; } +>Animal : Symbol(Animal, Decl(genericFunctionParametersConditionalType1.ts, 25, 68)) +>eat : Symbol(Animal.eat, Decl(genericFunctionParametersConditionalType1.ts, 27, 22)) + +declare class Cat extends Animal { meow(): void; } +>Cat : Symbol(Cat, Decl(genericFunctionParametersConditionalType1.ts, 27, 37)) +>Animal : Symbol(Animal, Decl(genericFunctionParametersConditionalType1.ts, 25, 68)) +>meow : Symbol(Cat.meow, Decl(genericFunctionParametersConditionalType1.ts, 28, 34)) + +declare class Dog extends Animal { bark(): void; } +>Dog : Symbol(Dog, Decl(genericFunctionParametersConditionalType1.ts, 28, 50)) +>Animal : Symbol(Animal, Decl(genericFunctionParametersConditionalType1.ts, 25, 68)) +>bark : Symbol(Dog.bark, Decl(genericFunctionParametersConditionalType1.ts, 29, 34)) + +type WithDistributiveConditionalDirectlyInParam = ( +>WithDistributiveConditionalDirectlyInParam : Symbol(WithDistributiveConditionalDirectlyInParam, Decl(genericFunctionParametersConditionalType1.ts, 29, 50)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 31, 51)) +>Cat : Symbol(Cat, Decl(genericFunctionParametersConditionalType1.ts, 27, 37)) +>Dog : Symbol(Dog, Decl(genericFunctionParametersConditionalType1.ts, 28, 50)) + + arg: T extends unknown ? T : never, +>arg : Symbol(arg, Decl(genericFunctionParametersConditionalType1.ts, 31, 72)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 31, 51)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 31, 51)) + +) => void; + +type Result1 = Parameters; +>Result1 : Symbol(Result1, Decl(genericFunctionParametersConditionalType1.ts, 33, 10)) +>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --)) +>WithDistributiveConditionalDirectlyInParam : Symbol(WithDistributiveConditionalDirectlyInParam, Decl(genericFunctionParametersConditionalType1.ts, 29, 50)) + +type WithDistributiveConditionalNested = ( +>WithDistributiveConditionalNested : Symbol(WithDistributiveConditionalNested, Decl(genericFunctionParametersConditionalType1.ts, 35, 70)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 37, 42)) +>Cat : Symbol(Cat, Decl(genericFunctionParametersConditionalType1.ts, 27, 37)) +>Dog : Symbol(Dog, Decl(genericFunctionParametersConditionalType1.ts, 28, 50)) + + arg: T extends unknown ? { animal: T } : never, +>arg : Symbol(arg, Decl(genericFunctionParametersConditionalType1.ts, 37, 63)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 37, 42)) +>animal : Symbol(animal, Decl(genericFunctionParametersConditionalType1.ts, 38, 28)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 37, 42)) + +) => void; + +type Result2 = Parameters; +>Result2 : Symbol(Result2, Decl(genericFunctionParametersConditionalType1.ts, 39, 10)) +>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --)) +>WithDistributiveConditionalNested : Symbol(WithDistributiveConditionalNested, Decl(genericFunctionParametersConditionalType1.ts, 35, 70)) + +type WithNonDistributiveConditionalNested = ( +>WithNonDistributiveConditionalNested : Symbol(WithNonDistributiveConditionalNested, Decl(genericFunctionParametersConditionalType1.ts, 41, 61)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 43, 45)) +>Cat : Symbol(Cat, Decl(genericFunctionParametersConditionalType1.ts, 27, 37)) +>Dog : Symbol(Dog, Decl(genericFunctionParametersConditionalType1.ts, 28, 50)) + + arg: [T] extends [unknown] ? { animal: T } : never, +>arg : Symbol(arg, Decl(genericFunctionParametersConditionalType1.ts, 43, 66)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 43, 45)) +>animal : Symbol(animal, Decl(genericFunctionParametersConditionalType1.ts, 44, 32)) +>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 43, 45)) + +) => void; + +type Result3 = Parameters; +>Result3 : Symbol(Result3, Decl(genericFunctionParametersConditionalType1.ts, 45, 10)) +>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --)) +>WithNonDistributiveConditionalNested : Symbol(WithNonDistributiveConditionalNested, Decl(genericFunctionParametersConditionalType1.ts, 41, 61)) + diff --git a/tests/baselines/reference/genericFunctionParametersConditionalType1.types b/tests/baselines/reference/genericFunctionParametersConditionalType1.types new file mode 100644 index 0000000000000..35467c015c815 --- /dev/null +++ b/tests/baselines/reference/genericFunctionParametersConditionalType1.types @@ -0,0 +1,131 @@ +//// [tests/cases/compiler/genericFunctionParametersConditionalType1.ts] //// + +=== genericFunctionParametersConditionalType1.ts === +// https://github.com/microsoft/TypeScript/issues/62079 + +export {}; + +interface _Map { + foo: { a: 123 } +>foo : { a: 123; } +> : ^^^^^ ^^^ +>a : 123 +> : ^^^ +} + +type ModuleSubType = "bar" & { brand: true }; +>ModuleSubType : ModuleSubType +> : ^^^^^^^^^^^^^ +>brand : true +> : ^^^^ +>true : true +> : ^^^^ + +type Map = _Map & Record +>Map : Map +> : ^^^ +>blah : string +> : ^^^^^^ + +type SubTypeGet< +>SubTypeGet : SubTypeGet +> : ^^^^^^^^^^^^^^^^^^^^^^^^ + + SubType extends string, + Map extends Record, +> = SubType extends unknown +? { type?: SubType } & Map[SubType] +>type : SubType | undefined +> : ^^^^^^^^^^^^^^^^^^^ + +: never; + +type TestParameters = Parameters<(arg: SubTypeGet) => void> +>TestParameters : [arg: { type?: ModuleSubType | undefined; } | ({ type?: "foo" | undefined; } & { a: 123; })] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ +>arg : SubTypeGet +> : ^^^^^^^^^^^^^^^^^^ + +declare class Test { +>Test : Test +> : ^^^^^^^ + + constructor(arg: SubTypeGet); +>arg : SubTypeGet +> : ^^^^^^^^^^^^^^^^^^ +} + +type TestConstructorParameters = ConstructorParameters; +>TestConstructorParameters : [arg: { type?: ModuleSubType | undefined; } | ({ type?: "foo" | undefined; } & { a: 123; })] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ +>Test : typeof Test +> : ^^^^^^^^^^^ + +declare class Animal { eat(): void; } +>Animal : Animal +> : ^^^^^^ +>eat : () => void +> : ^^^^^^ + +declare class Cat extends Animal { meow(): void; } +>Cat : Cat +> : ^^^ +>Animal : Animal +> : ^^^^^^ +>meow : () => void +> : ^^^^^^ + +declare class Dog extends Animal { bark(): void; } +>Dog : Dog +> : ^^^ +>Animal : Animal +> : ^^^^^^ +>bark : () => void +> : ^^^^^^ + +type WithDistributiveConditionalDirectlyInParam = ( +>WithDistributiveConditionalDirectlyInParam : WithDistributiveConditionalDirectlyInParam +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + arg: T extends unknown ? T : never, +>arg : T extends unknown ? T : never +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +) => void; + +type Result1 = Parameters; +>Result1 : [arg: Cat | Dog] +> : ^^^^^^^^^^^^^^^^ + +type WithDistributiveConditionalNested = ( +>WithDistributiveConditionalNested : WithDistributiveConditionalNested +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + arg: T extends unknown ? { animal: T } : never, +>arg : T extends unknown ? { animal: T; } : never +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ +>animal : T +> : ^ + +) => void; + +type Result2 = Parameters; +>Result2 : [arg: { animal: Cat; } | { animal: Dog; }] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +type WithNonDistributiveConditionalNested = ( +>WithNonDistributiveConditionalNested : WithNonDistributiveConditionalNested +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + arg: [T] extends [unknown] ? { animal: T } : never, +>arg : [T] extends [unknown] ? { animal: T; } : never +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ +>animal : T +> : ^ + +) => void; + +type Result3 = Parameters; +>Result3 : [arg: { animal: Cat | Dog; }] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/baselines/reference/typePredicateFreshLiteralWidening.symbols b/tests/baselines/reference/typePredicateFreshLiteralWidening.symbols index c4464e5d4bd39..e399cf4eb6388 100644 --- a/tests/baselines/reference/typePredicateFreshLiteralWidening.symbols +++ b/tests/baselines/reference/typePredicateFreshLiteralWidening.symbols @@ -88,9 +88,9 @@ const values1 = [item1, item2, item3].map(item => item.value); >item3 : Symbol(item3, Decl(typePredicateFreshLiteralWidening.ts, 19, 5)) >map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) >item : Symbol(item, Decl(typePredicateFreshLiteralWidening.ts, 24, 42)) ->item.value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 17, 33), Decl(typePredicateFreshLiteralWidening.ts, 18, 33), Decl(typePredicateFreshLiteralWidening.ts, 19, 33)) +>item.value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 15, 13)) >item : Symbol(item, Decl(typePredicateFreshLiteralWidening.ts, 24, 42)) ->value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 17, 33), Decl(typePredicateFreshLiteralWidening.ts, 18, 33), Decl(typePredicateFreshLiteralWidening.ts, 19, 33)) +>value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 15, 13)) const filteredValues1 = values1.filter(isNotNull); >filteredValues1 : Symbol(filteredValues1, Decl(typePredicateFreshLiteralWidening.ts, 25, 5)) diff --git a/tests/baselines/reference/typePredicateFreshLiteralWidening.types b/tests/baselines/reference/typePredicateFreshLiteralWidening.types index 83f8935c32f38..785365ac329b5 100644 --- a/tests/baselines/reference/typePredicateFreshLiteralWidening.types +++ b/tests/baselines/reference/typePredicateFreshLiteralWidening.types @@ -51,34 +51,34 @@ type Item = { value: string | null }; > : ^^^^^^^^^^^^^ const item1 = satisfies()({ value: "1" }); ->item1 : { value: "1"; } -> : ^^^^^^^^^^^^^^^ ->satisfies()({ value: "1" }) : { value: "1"; } -> : ^^^^^^^^^^^^^^^ +>item1 : { value: Narrow; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>satisfies()({ value: "1" }) : { value: Narrow; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >satisfies() : (narrow: Narrow) => Narrow > : ^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >satisfies : () => (narrow: Narrow) => Narrow > : ^ ^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^ ->{ value: "1" } : { value: "1"; } -> : ^^^^^^^^^^^^^^^ ->value : "1" -> : ^^^ +>{ value: "1" } : { value: string; } +> : ^^^^^^^^^^^^^^^^^^ +>value : string +> : ^^^^^^ >"1" : "1" > : ^^^ const item2 = satisfies()({ value: "2" }); ->item2 : { value: "2"; } -> : ^^^^^^^^^^^^^^^ ->satisfies()({ value: "2" }) : { value: "2"; } -> : ^^^^^^^^^^^^^^^ +>item2 : { value: Narrow; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>satisfies()({ value: "2" }) : { value: Narrow; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >satisfies() : (narrow: Narrow) => Narrow > : ^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >satisfies : () => (narrow: Narrow) => Narrow > : ^ ^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^ ->{ value: "2" } : { value: "2"; } -> : ^^^^^^^^^^^^^^^ ->value : "2" -> : ^^^ +>{ value: "2" } : { value: string; } +> : ^^^^^^^^^^^^^^^^^^ +>value : string +> : ^^^^^^ >"2" : "2" > : ^^^ @@ -121,44 +121,44 @@ const filteredValues2 = values2.filter(isNotNull); > : ^ ^^ ^^ ^^^^^ const values1 = [item1, item2, item3].map(item => item.value); ->values1 : ("1" | "2" | null)[] -> : ^^^^^^^^^^^^^^^^^^^^ ->[item1, item2, item3].map(item => item.value) : ("1" | "2" | null)[] -> : ^^^^^^^^^^^^^^^^^^^^ ->[item1, item2, item3].map : (callbackfn: (value: { value: "1"; } | { value: "2"; } | { value: null; }, index: number, array: ({ value: "1"; } | { value: "2"; } | { value: null; })[]) => U, thisArg?: any) => U[] -> : ^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ ->[item1, item2, item3] : ({ value: "1"; } | { value: "2"; } | { value: null; })[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->item1 : { value: "1"; } -> : ^^^^^^^^^^^^^^^ ->item2 : { value: "2"; } -> : ^^^^^^^^^^^^^^^ +>values1 : Narrow[] +> : ^^^^^^^^^^^^^^^^^^^^^^^ +>[item1, item2, item3].map(item => item.value) : Narrow[] +> : ^^^^^^^^^^^^^^^^^^^^^^^ +>[item1, item2, item3].map : (callbackfn: (value: { value: Narrow; }, index: number, array: { value: Narrow; }[]) => U, thisArg?: any) => U[] +> : ^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ +>[item1, item2, item3] : { value: Narrow; }[] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>item1 : { value: Narrow; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>item2 : { value: Narrow; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >item3 : { value: null; } > : ^^^^^^^^^^^^^^^^ ->map : (callbackfn: (value: { value: "1"; } | { value: "2"; } | { value: null; }, index: number, array: ({ value: "1"; } | { value: "2"; } | { value: null; })[]) => U, thisArg?: any) => U[] -> : ^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ ->item => item.value : (item: { value: "1"; } | { value: "2"; } | { value: null; }) => "1" | "2" | null -> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->item : { value: "1"; } | { value: "2"; } | { value: null; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->item.value : "1" | "2" | null -> : ^^^^^^^^^^^^^^^^ ->item : { value: "1"; } | { value: "2"; } | { value: null; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->value : "1" | "2" | null -> : ^^^^^^^^^^^^^^^^ +>map : (callbackfn: (value: { value: Narrow; }, index: number, array: { value: Narrow; }[]) => U, thisArg?: any) => U[] +> : ^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ +>item => item.value : (item: { value: Narrow; }) => Narrow +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>item : { value: Narrow; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>item.value : Narrow +> : ^^^^^^^^^^^^^^^^^^^^^ +>item : { value: Narrow; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>value : Narrow +> : ^^^^^^^^^^^^^^^^^^^^^ const filteredValues1 = values1.filter(isNotNull); ->filteredValues1 : ("1" | "2")[] -> : ^^^^^^^^^^^^^ ->values1.filter(isNotNull) : ("1" | "2")[] -> : ^^^^^^^^^^^^^ ->values1.filter : { (predicate: (value: "1" | "2" | null, index: number, array: ("1" | "2" | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: "1" | "2" | null, index: number, array: ("1" | "2" | null)[]) => unknown, thisArg?: any): ("1" | "2" | null)[]; } -> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ ->values1 : ("1" | "2" | null)[] -> : ^^^^^^^^^^^^^^^^^^^^ ->filter : { (predicate: (value: "1" | "2" | null, index: number, array: ("1" | "2" | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: "1" | "2" | null, index: number, array: ("1" | "2" | null)[]) => unknown, thisArg?: any): ("1" | "2" | null)[]; } -> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>filteredValues1 : string[] +> : ^^^^^^^^ +>values1.filter(isNotNull) : string[] +> : ^^^^^^^^ +>values1.filter : { >(predicate: (value: Narrow, index: number, array: Narrow[]) => value is S, thisArg?: any): S[]; (predicate: (value: Narrow, index: number, array: Narrow[]) => unknown, thisArg?: any): Narrow[]; } +> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>values1 : Narrow[] +> : ^^^^^^^^^^^^^^^^^^^^^^^ +>filter : { >(predicate: (value: Narrow, index: number, array: Narrow[]) => value is S, thisArg?: any): S[]; (predicate: (value: Narrow, index: number, array: Narrow[]) => unknown, thisArg?: any): Narrow[]; } +> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >isNotNull : (value: T | null) => value is T > : ^ ^^ ^^ ^^^^^ diff --git a/tests/cases/compiler/genericFunctionParametersConditionalType1.ts b/tests/cases/compiler/genericFunctionParametersConditionalType1.ts new file mode 100644 index 0000000000000..7bcce5c36b83a --- /dev/null +++ b/tests/cases/compiler/genericFunctionParametersConditionalType1.ts @@ -0,0 +1,51 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/62079 + +export {}; + +interface _Map { + foo: { a: 123 } +} + +type ModuleSubType = "bar" & { brand: true }; + +type Map = _Map & Record + +type SubTypeGet< + SubType extends string, + Map extends Record, +> = SubType extends unknown +? { type?: SubType } & Map[SubType] +: never; + +type TestParameters = Parameters<(arg: SubTypeGet) => void> + +declare class Test { + constructor(arg: SubTypeGet); +} + +type TestConstructorParameters = ConstructorParameters; + +declare class Animal { eat(): void; } +declare class Cat extends Animal { meow(): void; } +declare class Dog extends Animal { bark(): void; } + +type WithDistributiveConditionalDirectlyInParam = ( + arg: T extends unknown ? T : never, +) => void; + +type Result1 = Parameters; + +type WithDistributiveConditionalNested = ( + arg: T extends unknown ? { animal: T } : never, +) => void; + +type Result2 = Parameters; + +type WithNonDistributiveConditionalNested = ( + arg: [T] extends [unknown] ? { animal: T } : never, +) => void; + +type Result3 = Parameters; From cf7ea8af25a768158c1f68856c6cd8c865cf7b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 20 Jul 2025 12:04:50 +0200 Subject: [PATCH 2/6] keep those inferences when "external" candidates are found --- src/compiler/checker.ts | 10 +- .../typePredicateFreshLiteralWidening.symbols | 4 +- .../typePredicateFreshLiteralWidening.types | 100 +++++++++--------- 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 30ec6c749bca1..2ada5f6eed018 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26784,14 +26784,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (candidate === blockedStringType) { return; } - const combinedPriority = priority | (inference.individualPriority || InferencePriority.None) - if (inference.priority === undefined || combinedPriority < inference.priority) { + const combinedPriority = priority | (inference.individualPriority || InferencePriority.None); + if (inference.priority === undefined || priority < (inference.priority & ~InferencePriority.DistributiveConditional)) { inference.candidates = undefined; inference.contraCandidates = undefined; inference.topLevel = true; inference.priority = combinedPriority; } - if (combinedPriority === inference.priority) { + if (priority === (inference.priority & ~InferencePriority.DistributiveConditional)) { + if (inference.priority !== combinedPriority) { + inference.priority = combinedPriority; + clearCachedInferences(inferences); + } // We make contravariant inferences only if we are in a pure contravariant position, // i.e. only if we have not descended into a bivariant position. if (contravariant && !bivariant) { diff --git a/tests/baselines/reference/typePredicateFreshLiteralWidening.symbols b/tests/baselines/reference/typePredicateFreshLiteralWidening.symbols index e399cf4eb6388..c4464e5d4bd39 100644 --- a/tests/baselines/reference/typePredicateFreshLiteralWidening.symbols +++ b/tests/baselines/reference/typePredicateFreshLiteralWidening.symbols @@ -88,9 +88,9 @@ const values1 = [item1, item2, item3].map(item => item.value); >item3 : Symbol(item3, Decl(typePredicateFreshLiteralWidening.ts, 19, 5)) >map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) >item : Symbol(item, Decl(typePredicateFreshLiteralWidening.ts, 24, 42)) ->item.value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 15, 13)) +>item.value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 17, 33), Decl(typePredicateFreshLiteralWidening.ts, 18, 33), Decl(typePredicateFreshLiteralWidening.ts, 19, 33)) >item : Symbol(item, Decl(typePredicateFreshLiteralWidening.ts, 24, 42)) ->value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 15, 13)) +>value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 17, 33), Decl(typePredicateFreshLiteralWidening.ts, 18, 33), Decl(typePredicateFreshLiteralWidening.ts, 19, 33)) const filteredValues1 = values1.filter(isNotNull); >filteredValues1 : Symbol(filteredValues1, Decl(typePredicateFreshLiteralWidening.ts, 25, 5)) diff --git a/tests/baselines/reference/typePredicateFreshLiteralWidening.types b/tests/baselines/reference/typePredicateFreshLiteralWidening.types index 785365ac329b5..83f8935c32f38 100644 --- a/tests/baselines/reference/typePredicateFreshLiteralWidening.types +++ b/tests/baselines/reference/typePredicateFreshLiteralWidening.types @@ -51,34 +51,34 @@ type Item = { value: string | null }; > : ^^^^^^^^^^^^^ const item1 = satisfies()({ value: "1" }); ->item1 : { value: Narrow; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->satisfies()({ value: "1" }) : { value: Narrow; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>item1 : { value: "1"; } +> : ^^^^^^^^^^^^^^^ +>satisfies()({ value: "1" }) : { value: "1"; } +> : ^^^^^^^^^^^^^^^ >satisfies() : (narrow: Narrow) => Narrow > : ^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >satisfies : () => (narrow: Narrow) => Narrow > : ^ ^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^ ->{ value: "1" } : { value: string; } -> : ^^^^^^^^^^^^^^^^^^ ->value : string -> : ^^^^^^ +>{ value: "1" } : { value: "1"; } +> : ^^^^^^^^^^^^^^^ +>value : "1" +> : ^^^ >"1" : "1" > : ^^^ const item2 = satisfies()({ value: "2" }); ->item2 : { value: Narrow; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->satisfies()({ value: "2" }) : { value: Narrow; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>item2 : { value: "2"; } +> : ^^^^^^^^^^^^^^^ +>satisfies()({ value: "2" }) : { value: "2"; } +> : ^^^^^^^^^^^^^^^ >satisfies() : (narrow: Narrow) => Narrow > : ^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >satisfies : () => (narrow: Narrow) => Narrow > : ^ ^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^ ->{ value: "2" } : { value: string; } -> : ^^^^^^^^^^^^^^^^^^ ->value : string -> : ^^^^^^ +>{ value: "2" } : { value: "2"; } +> : ^^^^^^^^^^^^^^^ +>value : "2" +> : ^^^ >"2" : "2" > : ^^^ @@ -121,44 +121,44 @@ const filteredValues2 = values2.filter(isNotNull); > : ^ ^^ ^^ ^^^^^ const values1 = [item1, item2, item3].map(item => item.value); ->values1 : Narrow[] -> : ^^^^^^^^^^^^^^^^^^^^^^^ ->[item1, item2, item3].map(item => item.value) : Narrow[] -> : ^^^^^^^^^^^^^^^^^^^^^^^ ->[item1, item2, item3].map : (callbackfn: (value: { value: Narrow; }, index: number, array: { value: Narrow; }[]) => U, thisArg?: any) => U[] -> : ^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ ->[item1, item2, item3] : { value: Narrow; }[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->item1 : { value: Narrow; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->item2 : { value: Narrow; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>values1 : ("1" | "2" | null)[] +> : ^^^^^^^^^^^^^^^^^^^^ +>[item1, item2, item3].map(item => item.value) : ("1" | "2" | null)[] +> : ^^^^^^^^^^^^^^^^^^^^ +>[item1, item2, item3].map : (callbackfn: (value: { value: "1"; } | { value: "2"; } | { value: null; }, index: number, array: ({ value: "1"; } | { value: "2"; } | { value: null; })[]) => U, thisArg?: any) => U[] +> : ^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ +>[item1, item2, item3] : ({ value: "1"; } | { value: "2"; } | { value: null; })[] +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>item1 : { value: "1"; } +> : ^^^^^^^^^^^^^^^ +>item2 : { value: "2"; } +> : ^^^^^^^^^^^^^^^ >item3 : { value: null; } > : ^^^^^^^^^^^^^^^^ ->map : (callbackfn: (value: { value: Narrow; }, index: number, array: { value: Narrow; }[]) => U, thisArg?: any) => U[] -> : ^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ ->item => item.value : (item: { value: Narrow; }) => Narrow -> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->item : { value: Narrow; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->item.value : Narrow -> : ^^^^^^^^^^^^^^^^^^^^^ ->item : { value: Narrow; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->value : Narrow -> : ^^^^^^^^^^^^^^^^^^^^^ +>map : (callbackfn: (value: { value: "1"; } | { value: "2"; } | { value: null; }, index: number, array: ({ value: "1"; } | { value: "2"; } | { value: null; })[]) => U, thisArg?: any) => U[] +> : ^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^ +>item => item.value : (item: { value: "1"; } | { value: "2"; } | { value: null; }) => "1" | "2" | null +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>item : { value: "1"; } | { value: "2"; } | { value: null; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>item.value : "1" | "2" | null +> : ^^^^^^^^^^^^^^^^ +>item : { value: "1"; } | { value: "2"; } | { value: null; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>value : "1" | "2" | null +> : ^^^^^^^^^^^^^^^^ const filteredValues1 = values1.filter(isNotNull); ->filteredValues1 : string[] -> : ^^^^^^^^ ->values1.filter(isNotNull) : string[] -> : ^^^^^^^^ ->values1.filter : { >(predicate: (value: Narrow, index: number, array: Narrow[]) => value is S, thisArg?: any): S[]; (predicate: (value: Narrow, index: number, array: Narrow[]) => unknown, thisArg?: any): Narrow[]; } -> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->values1 : Narrow[] -> : ^^^^^^^^^^^^^^^^^^^^^^^ ->filter : { >(predicate: (value: Narrow, index: number, array: Narrow[]) => value is S, thisArg?: any): S[]; (predicate: (value: Narrow, index: number, array: Narrow[]) => unknown, thisArg?: any): Narrow[]; } -> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>filteredValues1 : ("1" | "2")[] +> : ^^^^^^^^^^^^^ +>values1.filter(isNotNull) : ("1" | "2")[] +> : ^^^^^^^^^^^^^ +>values1.filter : { (predicate: (value: "1" | "2" | null, index: number, array: ("1" | "2" | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: "1" | "2" | null, index: number, array: ("1" | "2" | null)[]) => unknown, thisArg?: any): ("1" | "2" | null)[]; } +> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>values1 : ("1" | "2" | null)[] +> : ^^^^^^^^^^^^^^^^^^^^ +>filter : { (predicate: (value: "1" | "2" | null, index: number, array: ("1" | "2" | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: "1" | "2" | null, index: number, array: ("1" | "2" | null)[]) => unknown, thisArg?: any): ("1" | "2" | null)[]; } +> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ >isNotNull : (value: T | null) => value is T > : ^ ^^ ^^ ^^^^^ From ed80c21e47b6366db914c88f1e99c429837a4a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 20 Jul 2025 12:51:37 +0200 Subject: [PATCH 3/6] fixup whitespace --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8bbfa50a90c84..5b0607f38b7be 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7103,7 +7103,7 @@ export const enum InferencePriority { ReturnType = 1 << 7, // Inference made from return type of generic function LiteralKeyof = 1 << 8, // Inference made from a string literal to a keyof T DistributiveConditional = 1 << 9, - NoConstraints = 1 << 10, // Don't infer from constraints of instantiable types + NoConstraints = 1 << 10, // Don't infer from constraints of instantiable types AlwaysStrict = 1 << 11, // Always use strict rules for contravariant inferences MaxValue = 1 << 12, // Seed for inference priority tracking From 3a10dd21a43b2da8fda69d03b3c25eb90f17380d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 20 Jul 2025 14:35:30 +0200 Subject: [PATCH 4/6] add extra test case --- src/compiler/checker.ts | 2 +- ...nferToDistributiveConditionalType1.symbols | 38 ++++++++++++++ .../inferToDistributiveConditionalType1.types | 51 +++++++++++++++++++ .../inferToDistributiveConditionalType1.ts | 10 ++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/inferToDistributiveConditionalType1.symbols create mode 100644 tests/baselines/reference/inferToDistributiveConditionalType1.types create mode 100644 tests/cases/compiler/inferToDistributiveConditionalType1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2ada5f6eed018..f8a6a752a6f1e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27162,7 +27162,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function inferToConditionalType(source: Type, target: ConditionalType) { const info = target.root.isDistributive ? getInferenceInfoForType(getActualTypeVariable(target.checkType)) : undefined; - const saveIndividualPriority = info?.individualPriority + const saveIndividualPriority = info?.individualPriority; if (info) { info.individualPriority = (info.individualPriority || InferencePriority.None) | InferencePriority.DistributiveConditional; } diff --git a/tests/baselines/reference/inferToDistributiveConditionalType1.symbols b/tests/baselines/reference/inferToDistributiveConditionalType1.symbols new file mode 100644 index 0000000000000..4f000ccc00536 --- /dev/null +++ b/tests/baselines/reference/inferToDistributiveConditionalType1.symbols @@ -0,0 +1,38 @@ +//// [tests/cases/compiler/inferToDistributiveConditionalType1.ts] //// + +=== inferToDistributiveConditionalType1.ts === +declare class Animal { eat(): void; } +>Animal : Symbol(Animal, Decl(inferToDistributiveConditionalType1.ts, 0, 0)) +>eat : Symbol(Animal.eat, Decl(inferToDistributiveConditionalType1.ts, 0, 22)) + +declare class Cat extends Animal { meow(): void; } +>Cat : Symbol(Cat, Decl(inferToDistributiveConditionalType1.ts, 0, 37)) +>Animal : Symbol(Animal, Decl(inferToDistributiveConditionalType1.ts, 0, 0)) +>meow : Symbol(Cat.meow, Decl(inferToDistributiveConditionalType1.ts, 1, 34)) + +declare class Dog extends Animal { bark(): void; } +>Dog : Symbol(Dog, Decl(inferToDistributiveConditionalType1.ts, 1, 50)) +>Animal : Symbol(Animal, Decl(inferToDistributiveConditionalType1.ts, 0, 0)) +>bark : Symbol(Dog.bark, Decl(inferToDistributiveConditionalType1.ts, 2, 34)) + +declare function test1(a: T extends unknown ? { prop: T } : never): T; +>test1 : Symbol(test1, Decl(inferToDistributiveConditionalType1.ts, 2, 50)) +>T : Symbol(T, Decl(inferToDistributiveConditionalType1.ts, 4, 23)) +>a : Symbol(a, Decl(inferToDistributiveConditionalType1.ts, 4, 26)) +>T : Symbol(T, Decl(inferToDistributiveConditionalType1.ts, 4, 23)) +>prop : Symbol(prop, Decl(inferToDistributiveConditionalType1.ts, 4, 50)) +>T : Symbol(T, Decl(inferToDistributiveConditionalType1.ts, 4, 23)) +>T : Symbol(T, Decl(inferToDistributiveConditionalType1.ts, 4, 23)) + +declare const arg1: { prop: Dog } | { prop: Cat }; +>arg1 : Symbol(arg1, Decl(inferToDistributiveConditionalType1.ts, 5, 13)) +>prop : Symbol(prop, Decl(inferToDistributiveConditionalType1.ts, 5, 21)) +>Dog : Symbol(Dog, Decl(inferToDistributiveConditionalType1.ts, 1, 50)) +>prop : Symbol(prop, Decl(inferToDistributiveConditionalType1.ts, 5, 37)) +>Cat : Symbol(Cat, Decl(inferToDistributiveConditionalType1.ts, 0, 37)) + +const result1 = test1(arg1); +>result1 : Symbol(result1, Decl(inferToDistributiveConditionalType1.ts, 6, 5)) +>test1 : Symbol(test1, Decl(inferToDistributiveConditionalType1.ts, 2, 50)) +>arg1 : Symbol(arg1, Decl(inferToDistributiveConditionalType1.ts, 5, 13)) + diff --git a/tests/baselines/reference/inferToDistributiveConditionalType1.types b/tests/baselines/reference/inferToDistributiveConditionalType1.types new file mode 100644 index 0000000000000..83309450eb075 --- /dev/null +++ b/tests/baselines/reference/inferToDistributiveConditionalType1.types @@ -0,0 +1,51 @@ +//// [tests/cases/compiler/inferToDistributiveConditionalType1.ts] //// + +=== inferToDistributiveConditionalType1.ts === +declare class Animal { eat(): void; } +>Animal : Animal +> : ^^^^^^ +>eat : () => void +> : ^^^^^^ + +declare class Cat extends Animal { meow(): void; } +>Cat : Cat +> : ^^^ +>Animal : Animal +> : ^^^^^^ +>meow : () => void +> : ^^^^^^ + +declare class Dog extends Animal { bark(): void; } +>Dog : Dog +> : ^^^ +>Animal : Animal +> : ^^^^^^ +>bark : () => void +> : ^^^^^^ + +declare function test1(a: T extends unknown ? { prop: T } : never): T; +>test1 : (a: T extends unknown ? { prop: T; } : never) => T +> : ^ ^^ ^^ ^^^^^ +>a : T extends unknown ? { prop: T; } : never +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ +>prop : T +> : ^ + +declare const arg1: { prop: Dog } | { prop: Cat }; +>arg1 : { prop: Dog; } | { prop: Cat; } +> : ^^^^^^^^ ^^^^^^^^^^^^^^ ^^^ +>prop : Dog +> : ^^^ +>prop : Cat +> : ^^^ + +const result1 = test1(arg1); +>result1 : Cat | Dog +> : ^^^^^^^^^ +>test1(arg1) : Cat | Dog +> : ^^^^^^^^^ +>test1 : (a: T extends unknown ? { prop: T; } : never) => T +> : ^ ^^ ^^ ^^^^^ +>arg1 : { prop: Dog; } | { prop: Cat; } +> : ^^^^^^^^ ^^^^^^^^^^^^^^ ^^^ + diff --git a/tests/cases/compiler/inferToDistributiveConditionalType1.ts b/tests/cases/compiler/inferToDistributiveConditionalType1.ts new file mode 100644 index 0000000000000..fb93b9a0252fe --- /dev/null +++ b/tests/cases/compiler/inferToDistributiveConditionalType1.ts @@ -0,0 +1,10 @@ +// @strict: true +// @noEmit: true + +declare class Animal { eat(): void; } +declare class Cat extends Animal { meow(): void; } +declare class Dog extends Animal { bark(): void; } + +declare function test1(a: T extends unknown ? { prop: T } : never): T; +declare const arg1: { prop: Dog } | { prop: Cat }; +const result1 = test1(arg1); From c67b5de9ea245fa5d5071c03d6b76427dd1d3934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 19 Aug 2025 11:43:14 +0200 Subject: [PATCH 5/6] don't infer from multiple distributive conditional types for now --- src/compiler/checker.ts | 2 +- ...iveConditionalTypeAndOtherTargets1.symbols | 170 ++++++++++++++++++ ...utiveConditionalTypeAndOtherTargets1.types | 129 +++++++++++++ ...ributiveConditionalTypeAndOtherTargets1.ts | 58 ++++++ 4 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/inferToDistributiveConditionalTypeAndOtherTargets1.symbols create mode 100644 tests/baselines/reference/inferToDistributiveConditionalTypeAndOtherTargets1.types create mode 100644 tests/cases/compiler/inferToDistributiveConditionalTypeAndOtherTargets1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f8a6a752a6f1e..4eef303b5eee4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27163,7 +27163,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function inferToConditionalType(source: Type, target: ConditionalType) { const info = target.root.isDistributive ? getInferenceInfoForType(getActualTypeVariable(target.checkType)) : undefined; const saveIndividualPriority = info?.individualPriority; - if (info) { + if (info && !hasInferenceCandidates(info)) { info.individualPriority = (info.individualPriority || InferencePriority.None) | InferencePriority.DistributiveConditional; } if (source.flags & TypeFlags.Conditional) { diff --git a/tests/baselines/reference/inferToDistributiveConditionalTypeAndOtherTargets1.symbols b/tests/baselines/reference/inferToDistributiveConditionalTypeAndOtherTargets1.symbols new file mode 100644 index 0000000000000..a1bc70bb7a965 --- /dev/null +++ b/tests/baselines/reference/inferToDistributiveConditionalTypeAndOtherTargets1.symbols @@ -0,0 +1,170 @@ +//// [tests/cases/compiler/inferToDistributiveConditionalTypeAndOtherTargets1.ts] //// + +=== inferToDistributiveConditionalTypeAndOtherTargets1.ts === +type NonUndefinedGuard = T extends undefined ? never : T; +>NonUndefinedGuard : Symbol(NonUndefinedGuard, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 0, 0)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 0, 23)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 0, 23)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 0, 23)) + +declare const RefSymbol: unique symbol; +>RefSymbol : Symbol(RefSymbol, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 2, 13)) + +interface Ref { +>Ref : Symbol(Ref, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 2, 39)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 4, 14)) +>S : Symbol(S, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 4, 22)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 4, 14)) + + get value(): T; +>value : Symbol(Ref.value, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 4, 31), Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 5, 17)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 4, 14)) + + set value(_: S); +>value : Symbol(Ref.value, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 4, 31), Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 5, 17)) +>_ : Symbol(_, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 6, 12)) +>S : Symbol(S, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 4, 22)) + + [RefSymbol]: true; +>[RefSymbol] : Symbol(Ref[RefSymbol], Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 6, 18)) +>RefSymbol : Symbol(RefSymbol, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 2, 13)) +} + +type MaybeRef = Ref | T; +>MaybeRef : Symbol(MaybeRef, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 8, 1)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 10, 14)) +>Ref : Symbol(Ref, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 2, 39)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 10, 14)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 10, 14)) + +type MaybeRefDeep = MaybeRef< +>MaybeRefDeep : Symbol(MaybeRefDeep, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 10, 30)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 12, 18)) +>MaybeRef : Symbol(MaybeRef, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 8, 1)) + + T extends Function +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 12, 18)) +>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + ? T +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 12, 18)) + + : T extends object +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 12, 18)) + + ? { + [Property in keyof T]: MaybeRefDeep; +>Property : Symbol(Property, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 17, 9)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 12, 18)) +>MaybeRefDeep : Symbol(MaybeRefDeep, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 10, 30)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 12, 18)) +>Property : Symbol(Property, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 17, 9)) + } + : T +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 12, 18)) + +>; + +type QueryFunction = () => T | Promise; +>QueryFunction : Symbol(QueryFunction, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 20, 2)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 22, 19)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 22, 19)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 22, 19)) + +type InitialDataFunction = () => T | undefined; +>InitialDataFunction : Symbol(InitialDataFunction, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 22, 55)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 24, 25)) +>T : Symbol(T, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 24, 25)) + +interface QueryOptions { +>QueryOptions : Symbol(QueryOptions, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 24, 50)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 26, 23)) +>TData : Symbol(TData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 26, 46)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 26, 23)) + + queryFn?: QueryFunction; +>queryFn : Symbol(QueryOptions.queryFn, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 26, 70)) +>QueryFunction : Symbol(QueryFunction, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 20, 2)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 26, 23)) + + initialData?: TData | InitialDataFunction; +>initialData : Symbol(QueryOptions.initialData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 27, 40)) +>TData : Symbol(TData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 26, 46)) +>InitialDataFunction : Symbol(InitialDataFunction, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 22, 55)) +>TData : Symbol(TData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 26, 46)) +} + +type UseQueryOptions = { +>UseQueryOptions : Symbol(UseQueryOptions, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 29, 1)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 31, 21)) +>TQueryData : Symbol(TQueryData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 31, 44)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 31, 21)) + + [Property in keyof QueryOptions]: MaybeRefDeep< +>Property : Symbol(Property, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 32, 3)) +>QueryOptions : Symbol(QueryOptions, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 24, 50)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 31, 21)) +>TQueryData : Symbol(TQueryData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 31, 44)) +>MaybeRefDeep : Symbol(MaybeRefDeep, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 10, 30)) + + QueryOptions[Property] +>QueryOptions : Symbol(QueryOptions, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 24, 50)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 31, 21)) +>TQueryData : Symbol(TQueryData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 31, 44)) +>Property : Symbol(Property, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 32, 3)) + + >; +}; + +type DefinedInitialQueryOptions = UseQueryOptions< +>DefinedInitialQueryOptions : Symbol(DefinedInitialQueryOptions, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 35, 2)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 37, 32)) +>UseQueryOptions : Symbol(UseQueryOptions, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 29, 1)) + + TQueryFnData, +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 37, 32)) + + TQueryFnData +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 37, 32)) + +> & { + initialData: +>initialData : Symbol(initialData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 40, 5)) + + | NonUndefinedGuard +>NonUndefinedGuard : Symbol(NonUndefinedGuard, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 0, 0)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 37, 32)) + + | (() => NonUndefinedGuard); +>NonUndefinedGuard : Symbol(NonUndefinedGuard, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 0, 0)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 37, 32)) + +}; + +declare function queryOptions( +>queryOptions : Symbol(queryOptions, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 44, 2)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 46, 30)) + + options: DefinedInitialQueryOptions, +>options : Symbol(options, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 46, 54)) +>DefinedInitialQueryOptions : Symbol(DefinedInitialQueryOptions, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 35, 2)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 46, 30)) + +): DefinedInitialQueryOptions; +>DefinedInitialQueryOptions : Symbol(DefinedInitialQueryOptions, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 35, 2)) +>TQueryFnData : Symbol(TQueryFnData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 46, 30)) + +const result = queryOptions({ +>result : Symbol(result, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 50, 5)) +>queryOptions : Symbol(queryOptions, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 44, 2)) + + initialData: () => ({ +>initialData : Symbol(initialData, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 50, 29)) + + wow: true, +>wow : Symbol(wow, Decl(inferToDistributiveConditionalTypeAndOtherTargets1.ts, 51, 23)) + + }), +}); + diff --git a/tests/baselines/reference/inferToDistributiveConditionalTypeAndOtherTargets1.types b/tests/baselines/reference/inferToDistributiveConditionalTypeAndOtherTargets1.types new file mode 100644 index 0000000000000..66b064290c909 --- /dev/null +++ b/tests/baselines/reference/inferToDistributiveConditionalTypeAndOtherTargets1.types @@ -0,0 +1,129 @@ +//// [tests/cases/compiler/inferToDistributiveConditionalTypeAndOtherTargets1.ts] //// + +=== inferToDistributiveConditionalTypeAndOtherTargets1.ts === +type NonUndefinedGuard = T extends undefined ? never : T; +>NonUndefinedGuard : NonUndefinedGuard +> : ^^^^^^^^^^^^^^^^^^^^ + +declare const RefSymbol: unique symbol; +>RefSymbol : unique symbol +> : ^^^^^^^^^^^^^ + +interface Ref { + get value(): T; +>value : T +> : ^ + + set value(_: S); +>value : T +> : ^ +>_ : S +> : ^ + + [RefSymbol]: true; +>[RefSymbol] : true +> : ^^^^ +>RefSymbol : unique symbol +> : ^^^^^^^^^^^^^ +>true : true +> : ^^^^ +} + +type MaybeRef = Ref | T; +>MaybeRef : MaybeRef +> : ^^^^^^^^^^^ + +type MaybeRefDeep = MaybeRef< +>MaybeRefDeep : MaybeRefDeep +> : ^^^^^^^^^^^^^^^ + + T extends Function + ? T + : T extends object + ? { + [Property in keyof T]: MaybeRefDeep; + } + : T +>; + +type QueryFunction = () => T | Promise; +>QueryFunction : QueryFunction +> : ^^^^^^^^^^^^^^^^ + +type InitialDataFunction = () => T | undefined; +>InitialDataFunction : InitialDataFunction +> : ^^^^^^^^^^^^^^^^^^^^^^ + +interface QueryOptions { + queryFn?: QueryFunction; +>queryFn : QueryFunction | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + initialData?: TData | InitialDataFunction; +>initialData : TData | InitialDataFunction | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} + +type UseQueryOptions = { +>UseQueryOptions : UseQueryOptions +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + [Property in keyof QueryOptions]: MaybeRefDeep< + QueryOptions[Property] + >; +}; + +type DefinedInitialQueryOptions = UseQueryOptions< +>DefinedInitialQueryOptions : DefinedInitialQueryOptions +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + TQueryFnData, + TQueryFnData +> & { + initialData: +>initialData : NonUndefinedGuard | (() => NonUndefinedGuard) +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ + + | NonUndefinedGuard + | (() => NonUndefinedGuard); +}; + +declare function queryOptions( +>queryOptions : (options: DefinedInitialQueryOptions) => DefinedInitialQueryOptions +> : ^ ^^^^^^^^^^^^ ^^ ^^^^^ + + options: DefinedInitialQueryOptions, +>options : DefinedInitialQueryOptions +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +): DefinedInitialQueryOptions; + +const result = queryOptions({ +>result : DefinedInitialQueryOptions<{ wow: boolean; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>queryOptions({ initialData: () => ({ wow: true, }),}) : DefinedInitialQueryOptions<{ wow: boolean; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>queryOptions : (options: DefinedInitialQueryOptions) => DefinedInitialQueryOptions +> : ^ ^^^^^^^^^^^^ ^^ ^^^^^ +>{ initialData: () => ({ wow: true, }),} : { initialData: () => { wow: boolean; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + initialData: () => ({ +>initialData : () => { wow: boolean; } +> : ^^^^^^^^^^^^^^^^^^^^^^^ +>() => ({ wow: true, }) : () => { wow: boolean; } +> : ^^^^^^^^^^^^^^^^^^^^^^^ +>({ wow: true, }) : { wow: true; } +> : ^^^^^^^^^^^^^^ +>{ wow: true, } : { wow: true; } +> : ^^^^^^^^^^^^^^ + + wow: true, +>wow : true +> : ^^^^ +>true : true +> : ^^^^ + + }), +}); + diff --git a/tests/cases/compiler/inferToDistributiveConditionalTypeAndOtherTargets1.ts b/tests/cases/compiler/inferToDistributiveConditionalTypeAndOtherTargets1.ts new file mode 100644 index 0000000000000..6ccb06931a99f --- /dev/null +++ b/tests/cases/compiler/inferToDistributiveConditionalTypeAndOtherTargets1.ts @@ -0,0 +1,58 @@ +// @strict: true +// @noEmit: true + +type NonUndefinedGuard = T extends undefined ? never : T; + +declare const RefSymbol: unique symbol; + +interface Ref { + get value(): T; + set value(_: S); + [RefSymbol]: true; +} + +type MaybeRef = Ref | T; + +type MaybeRefDeep = MaybeRef< + T extends Function + ? T + : T extends object + ? { + [Property in keyof T]: MaybeRefDeep; + } + : T +>; + +type QueryFunction = () => T | Promise; + +type InitialDataFunction = () => T | undefined; + +interface QueryOptions { + queryFn?: QueryFunction; + initialData?: TData | InitialDataFunction; +} + +type UseQueryOptions = { + [Property in keyof QueryOptions]: MaybeRefDeep< + QueryOptions[Property] + >; +}; + +type DefinedInitialQueryOptions = UseQueryOptions< + TQueryFnData, + TQueryFnData +> & { + initialData: + | NonUndefinedGuard + | (() => NonUndefinedGuard); +}; + +declare function queryOptions( + options: DefinedInitialQueryOptions, +): DefinedInitialQueryOptions; + +const result = queryOptions({ + initialData: () => ({ + wow: true, + }), +}); From 80ad2530a42f43b3a35d5944f2c1742d8ac5f67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 20 Aug 2025 09:08:57 +0200 Subject: [PATCH 6/6] improve the fix --- src/compiler/checker.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c7e59df9b1929..5148b1d306c77 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26810,7 +26810,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { inference.priority = combinedPriority; } if (priority === (inference.priority & ~InferencePriority.DistributiveConditional)) { - if (inference.priority !== combinedPriority) { + // "upgrade" the priority (by essentially removing DistributiveConditional bit) of the inference without discarding the collected candidates + if (inference.priority > combinedPriority) { inference.priority = combinedPriority; clearCachedInferences(inferences); } @@ -27181,7 +27182,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function inferToConditionalType(source: Type, target: ConditionalType) { const info = target.root.isDistributive ? getInferenceInfoForType(getActualTypeVariable(target.checkType)) : undefined; const saveIndividualPriority = info?.individualPriority; - if (info && !hasInferenceCandidates(info)) { + if (info) { info.individualPriority = (info.individualPriority || InferencePriority.None) | InferencePriority.DistributiveConditional; } if (source.flags & TypeFlags.Conditional) {