Skip to content

Commit 09e5824

Browse files
committed
Combine inferences from distributive conditional types
1 parent 2778e84 commit 09e5824

8 files changed

Lines changed: 402 additions & 63 deletions

src/compiler/checker.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26784,13 +26784,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2678426784
if (candidate === blockedStringType) {
2678526785
return;
2678626786
}
26787-
if (inference.priority === undefined || priority < inference.priority) {
26787+
const combinedPriority = priority | (inference.individualPriority || InferencePriority.None)
26788+
if (inference.priority === undefined || combinedPriority < inference.priority) {
2678826789
inference.candidates = undefined;
2678926790
inference.contraCandidates = undefined;
2679026791
inference.topLevel = true;
26791-
inference.priority = priority;
26792+
inference.priority = combinedPriority;
2679226793
}
26793-
if (priority === inference.priority) {
26794+
if (combinedPriority === inference.priority) {
2679426795
// We make contravariant inferences only if we are in a pure contravariant position,
2679526796
// i.e. only if we have not descended into a bivariant position.
2679626797
if (contravariant && !bivariant) {
@@ -27156,6 +27157,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2715627157
}
2715727158

2715827159
function inferToConditionalType(source: Type, target: ConditionalType) {
27160+
const info = target.root.isDistributive ? getInferenceInfoForType(getActualTypeVariable(target.checkType)) : undefined;
27161+
const saveIndividualPriority = info?.individualPriority
27162+
if (info) {
27163+
info.individualPriority = (info.individualPriority || InferencePriority.None) | InferencePriority.DistributiveConditional;
27164+
}
2715927165
if (source.flags & TypeFlags.Conditional) {
2716027166
inferFromTypes((source as ConditionalType).checkType, target.checkType);
2716127167
inferFromTypes((source as ConditionalType).extendsType, target.extendsType);
@@ -27166,6 +27172,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2716627172
const targetTypes = [getTrueTypeFromConditionalType(target), getFalseTypeFromConditionalType(target)];
2716727173
inferToMultipleTypesWithPriority(source, targetTypes, target.flags, contravariant ? InferencePriority.ContravariantConditional : 0);
2716827174
}
27175+
if (info) {
27176+
info.individualPriority = saveIndividualPriority;
27177+
}
2716927178
}
2717027179

2717127180
function inferToTemplateLiteralType(source: Type, target: TemplateLiteralType) {

src/compiler/types.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7102,11 +7102,12 @@ export const enum InferencePriority {
71027102
ContravariantConditional = 1 << 6, // Conditional type in contravariant position
71037103
ReturnType = 1 << 7, // Inference made from return type of generic function
71047104
LiteralKeyof = 1 << 8, // Inference made from a string literal to a keyof T
7105-
NoConstraints = 1 << 9, // Don't infer from constraints of instantiable types
7106-
AlwaysStrict = 1 << 10, // Always use strict rules for contravariant inferences
7107-
MaxValue = 1 << 11, // Seed for inference priority tracking
7105+
DistributiveConditional = 1 << 9,
7106+
NoConstraints = 1 << 10, // Don't infer from constraints of instantiable types
7107+
AlwaysStrict = 1 << 11, // Always use strict rules for contravariant inferences
7108+
MaxValue = 1 << 12, // Seed for inference priority tracking
71087109

7109-
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
7110+
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof | DistributiveConditional, // These priorities imply that the resulting type should be a combination of all candidates
71107111
Circularity = -1, // Inference circularity (value less than all other priorities)
71117112
}
71127113

@@ -7118,6 +7119,7 @@ export interface InferenceInfo {
71187119
contraCandidates: Type[] | undefined; // Candidates in contravariant positions (or undefined)
71197120
inferredType?: Type; // Cache for resolved inferred type
71207121
priority?: InferencePriority; // Priority of current inference set
7122+
individualPriority?: InferencePriority;
71217123
topLevel: boolean; // True if all inferences are to top level occurrences
71227124
isFixed: boolean; // True if inferences are fixed
71237125
impliedArity?: number;

tests/baselines/reference/api/typescript.d.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6893,10 +6893,11 @@ declare namespace ts {
68936893
ContravariantConditional = 64,
68946894
ReturnType = 128,
68956895
LiteralKeyof = 256,
6896-
NoConstraints = 512,
6897-
AlwaysStrict = 1024,
6898-
MaxValue = 2048,
6899-
PriorityImpliesCombination = 416,
6896+
DistributiveConditional = 512,
6897+
NoConstraints = 1024,
6898+
AlwaysStrict = 2048,
6899+
MaxValue = 4096,
6900+
PriorityImpliesCombination = 928,
69006901
Circularity = -1,
69016902
}
69026903
interface FileExtensionInfo {
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//// [tests/cases/compiler/genericFunctionParametersConditionalType1.ts] ////
2+
3+
=== genericFunctionParametersConditionalType1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/62079
5+
6+
export {};
7+
8+
interface _Map {
9+
>_Map : Symbol(_Map, Decl(genericFunctionParametersConditionalType1.ts, 2, 10))
10+
11+
foo: { a: 123 }
12+
>foo : Symbol(_Map.foo, Decl(genericFunctionParametersConditionalType1.ts, 4, 16))
13+
>a : Symbol(a, Decl(genericFunctionParametersConditionalType1.ts, 5, 8))
14+
}
15+
16+
type ModuleSubType = "bar" & { brand: true };
17+
>ModuleSubType : Symbol(ModuleSubType, Decl(genericFunctionParametersConditionalType1.ts, 6, 1))
18+
>brand : Symbol(brand, Decl(genericFunctionParametersConditionalType1.ts, 8, 30))
19+
20+
type Map = _Map & Record<ModuleSubType, { blah: string }>
21+
>Map : Symbol(Map, Decl(genericFunctionParametersConditionalType1.ts, 8, 45))
22+
>_Map : Symbol(_Map, Decl(genericFunctionParametersConditionalType1.ts, 2, 10))
23+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
24+
>ModuleSubType : Symbol(ModuleSubType, Decl(genericFunctionParametersConditionalType1.ts, 6, 1))
25+
>blah : Symbol(blah, Decl(genericFunctionParametersConditionalType1.ts, 10, 41))
26+
27+
type SubTypeGet<
28+
>SubTypeGet : Symbol(SubTypeGet, Decl(genericFunctionParametersConditionalType1.ts, 10, 57))
29+
30+
SubType extends string,
31+
>SubType : Symbol(SubType, Decl(genericFunctionParametersConditionalType1.ts, 12, 16))
32+
33+
Map extends Record<SubType, unknown>,
34+
>Map : Symbol(Map, Decl(genericFunctionParametersConditionalType1.ts, 13, 25))
35+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
36+
>SubType : Symbol(SubType, Decl(genericFunctionParametersConditionalType1.ts, 12, 16))
37+
38+
> = SubType extends unknown
39+
>SubType : Symbol(SubType, Decl(genericFunctionParametersConditionalType1.ts, 12, 16))
40+
41+
? { type?: SubType } & Map[SubType]
42+
>type : Symbol(type, Decl(genericFunctionParametersConditionalType1.ts, 16, 3))
43+
>SubType : Symbol(SubType, Decl(genericFunctionParametersConditionalType1.ts, 12, 16))
44+
>Map : Symbol(Map, Decl(genericFunctionParametersConditionalType1.ts, 13, 25))
45+
>SubType : Symbol(SubType, Decl(genericFunctionParametersConditionalType1.ts, 12, 16))
46+
47+
: never;
48+
49+
type TestParameters = Parameters<<T extends "foo" | ModuleSubType>(arg: SubTypeGet<T, Map>) => void>
50+
>TestParameters : Symbol(TestParameters, Decl(genericFunctionParametersConditionalType1.ts, 17, 8))
51+
>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --))
52+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 19, 34))
53+
>ModuleSubType : Symbol(ModuleSubType, Decl(genericFunctionParametersConditionalType1.ts, 6, 1))
54+
>arg : Symbol(arg, Decl(genericFunctionParametersConditionalType1.ts, 19, 67))
55+
>SubTypeGet : Symbol(SubTypeGet, Decl(genericFunctionParametersConditionalType1.ts, 10, 57))
56+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 19, 34))
57+
>Map : Symbol(Map, Decl(genericFunctionParametersConditionalType1.ts, 8, 45))
58+
59+
declare class Test<T extends "foo" | ModuleSubType> {
60+
>Test : Symbol(Test, Decl(genericFunctionParametersConditionalType1.ts, 19, 100))
61+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 21, 19))
62+
>ModuleSubType : Symbol(ModuleSubType, Decl(genericFunctionParametersConditionalType1.ts, 6, 1))
63+
64+
constructor(arg: SubTypeGet<T, Map>);
65+
>arg : Symbol(arg, Decl(genericFunctionParametersConditionalType1.ts, 22, 14))
66+
>SubTypeGet : Symbol(SubTypeGet, Decl(genericFunctionParametersConditionalType1.ts, 10, 57))
67+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 21, 19))
68+
>Map : Symbol(Map, Decl(genericFunctionParametersConditionalType1.ts, 8, 45))
69+
}
70+
71+
type TestConstructorParameters = ConstructorParameters<typeof Test>;
72+
>TestConstructorParameters : Symbol(TestConstructorParameters, Decl(genericFunctionParametersConditionalType1.ts, 23, 1))
73+
>ConstructorParameters : Symbol(ConstructorParameters, Decl(lib.es5.d.ts, --, --))
74+
>Test : Symbol(Test, Decl(genericFunctionParametersConditionalType1.ts, 19, 100))
75+
76+
declare class Animal { eat(): void; }
77+
>Animal : Symbol(Animal, Decl(genericFunctionParametersConditionalType1.ts, 25, 68))
78+
>eat : Symbol(Animal.eat, Decl(genericFunctionParametersConditionalType1.ts, 27, 22))
79+
80+
declare class Cat extends Animal { meow(): void; }
81+
>Cat : Symbol(Cat, Decl(genericFunctionParametersConditionalType1.ts, 27, 37))
82+
>Animal : Symbol(Animal, Decl(genericFunctionParametersConditionalType1.ts, 25, 68))
83+
>meow : Symbol(Cat.meow, Decl(genericFunctionParametersConditionalType1.ts, 28, 34))
84+
85+
declare class Dog extends Animal { bark(): void; }
86+
>Dog : Symbol(Dog, Decl(genericFunctionParametersConditionalType1.ts, 28, 50))
87+
>Animal : Symbol(Animal, Decl(genericFunctionParametersConditionalType1.ts, 25, 68))
88+
>bark : Symbol(Dog.bark, Decl(genericFunctionParametersConditionalType1.ts, 29, 34))
89+
90+
type WithDistributiveConditionalDirectlyInParam = <T extends Cat | Dog>(
91+
>WithDistributiveConditionalDirectlyInParam : Symbol(WithDistributiveConditionalDirectlyInParam, Decl(genericFunctionParametersConditionalType1.ts, 29, 50))
92+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 31, 51))
93+
>Cat : Symbol(Cat, Decl(genericFunctionParametersConditionalType1.ts, 27, 37))
94+
>Dog : Symbol(Dog, Decl(genericFunctionParametersConditionalType1.ts, 28, 50))
95+
96+
arg: T extends unknown ? T : never,
97+
>arg : Symbol(arg, Decl(genericFunctionParametersConditionalType1.ts, 31, 72))
98+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 31, 51))
99+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 31, 51))
100+
101+
) => void;
102+
103+
type Result1 = Parameters<WithDistributiveConditionalDirectlyInParam>;
104+
>Result1 : Symbol(Result1, Decl(genericFunctionParametersConditionalType1.ts, 33, 10))
105+
>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --))
106+
>WithDistributiveConditionalDirectlyInParam : Symbol(WithDistributiveConditionalDirectlyInParam, Decl(genericFunctionParametersConditionalType1.ts, 29, 50))
107+
108+
type WithDistributiveConditionalNested = <T extends Cat | Dog>(
109+
>WithDistributiveConditionalNested : Symbol(WithDistributiveConditionalNested, Decl(genericFunctionParametersConditionalType1.ts, 35, 70))
110+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 37, 42))
111+
>Cat : Symbol(Cat, Decl(genericFunctionParametersConditionalType1.ts, 27, 37))
112+
>Dog : Symbol(Dog, Decl(genericFunctionParametersConditionalType1.ts, 28, 50))
113+
114+
arg: T extends unknown ? { animal: T } : never,
115+
>arg : Symbol(arg, Decl(genericFunctionParametersConditionalType1.ts, 37, 63))
116+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 37, 42))
117+
>animal : Symbol(animal, Decl(genericFunctionParametersConditionalType1.ts, 38, 28))
118+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 37, 42))
119+
120+
) => void;
121+
122+
type Result2 = Parameters<WithDistributiveConditionalNested>;
123+
>Result2 : Symbol(Result2, Decl(genericFunctionParametersConditionalType1.ts, 39, 10))
124+
>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --))
125+
>WithDistributiveConditionalNested : Symbol(WithDistributiveConditionalNested, Decl(genericFunctionParametersConditionalType1.ts, 35, 70))
126+
127+
type WithNonDistributiveConditionalNested = <T extends Cat | Dog>(
128+
>WithNonDistributiveConditionalNested : Symbol(WithNonDistributiveConditionalNested, Decl(genericFunctionParametersConditionalType1.ts, 41, 61))
129+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 43, 45))
130+
>Cat : Symbol(Cat, Decl(genericFunctionParametersConditionalType1.ts, 27, 37))
131+
>Dog : Symbol(Dog, Decl(genericFunctionParametersConditionalType1.ts, 28, 50))
132+
133+
arg: [T] extends [unknown] ? { animal: T } : never,
134+
>arg : Symbol(arg, Decl(genericFunctionParametersConditionalType1.ts, 43, 66))
135+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 43, 45))
136+
>animal : Symbol(animal, Decl(genericFunctionParametersConditionalType1.ts, 44, 32))
137+
>T : Symbol(T, Decl(genericFunctionParametersConditionalType1.ts, 43, 45))
138+
139+
) => void;
140+
141+
type Result3 = Parameters<WithNonDistributiveConditionalNested>;
142+
>Result3 : Symbol(Result3, Decl(genericFunctionParametersConditionalType1.ts, 45, 10))
143+
>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --))
144+
>WithNonDistributiveConditionalNested : Symbol(WithNonDistributiveConditionalNested, Decl(genericFunctionParametersConditionalType1.ts, 41, 61))
145+
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//// [tests/cases/compiler/genericFunctionParametersConditionalType1.ts] ////
2+
3+
=== genericFunctionParametersConditionalType1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/62079
5+
6+
export {};
7+
8+
interface _Map {
9+
foo: { a: 123 }
10+
>foo : { a: 123; }
11+
> : ^^^^^ ^^^
12+
>a : 123
13+
> : ^^^
14+
}
15+
16+
type ModuleSubType = "bar" & { brand: true };
17+
>ModuleSubType : ModuleSubType
18+
> : ^^^^^^^^^^^^^
19+
>brand : true
20+
> : ^^^^
21+
>true : true
22+
> : ^^^^
23+
24+
type Map = _Map & Record<ModuleSubType, { blah: string }>
25+
>Map : Map
26+
> : ^^^
27+
>blah : string
28+
> : ^^^^^^
29+
30+
type SubTypeGet<
31+
>SubTypeGet : SubTypeGet<SubType, Map>
32+
> : ^^^^^^^^^^^^^^^^^^^^^^^^
33+
34+
SubType extends string,
35+
Map extends Record<SubType, unknown>,
36+
> = SubType extends unknown
37+
? { type?: SubType } & Map[SubType]
38+
>type : SubType | undefined
39+
> : ^^^^^^^^^^^^^^^^^^^
40+
41+
: never;
42+
43+
type TestParameters = Parameters<<T extends "foo" | ModuleSubType>(arg: SubTypeGet<T, Map>) => void>
44+
>TestParameters : [arg: { type?: ModuleSubType | undefined; } | ({ type?: "foo" | undefined; } & { a: 123; })]
45+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^
46+
>arg : SubTypeGet<T, Map>
47+
> : ^^^^^^^^^^^^^^^^^^
48+
49+
declare class Test<T extends "foo" | ModuleSubType> {
50+
>Test : Test<T>
51+
> : ^^^^^^^
52+
53+
constructor(arg: SubTypeGet<T, Map>);
54+
>arg : SubTypeGet<T, Map>
55+
> : ^^^^^^^^^^^^^^^^^^
56+
}
57+
58+
type TestConstructorParameters = ConstructorParameters<typeof Test>;
59+
>TestConstructorParameters : [arg: { type?: ModuleSubType | undefined; } | ({ type?: "foo" | undefined; } & { a: 123; })]
60+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^
61+
>Test : typeof Test
62+
> : ^^^^^^^^^^^
63+
64+
declare class Animal { eat(): void; }
65+
>Animal : Animal
66+
> : ^^^^^^
67+
>eat : () => void
68+
> : ^^^^^^
69+
70+
declare class Cat extends Animal { meow(): void; }
71+
>Cat : Cat
72+
> : ^^^
73+
>Animal : Animal
74+
> : ^^^^^^
75+
>meow : () => void
76+
> : ^^^^^^
77+
78+
declare class Dog extends Animal { bark(): void; }
79+
>Dog : Dog
80+
> : ^^^
81+
>Animal : Animal
82+
> : ^^^^^^
83+
>bark : () => void
84+
> : ^^^^^^
85+
86+
type WithDistributiveConditionalDirectlyInParam = <T extends Cat | Dog>(
87+
>WithDistributiveConditionalDirectlyInParam : WithDistributiveConditionalDirectlyInParam
88+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
89+
90+
arg: T extends unknown ? T : never,
91+
>arg : T extends unknown ? T : never
92+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
93+
94+
) => void;
95+
96+
type Result1 = Parameters<WithDistributiveConditionalDirectlyInParam>;
97+
>Result1 : [arg: Cat | Dog]
98+
> : ^^^^^^^^^^^^^^^^
99+
100+
type WithDistributiveConditionalNested = <T extends Cat | Dog>(
101+
>WithDistributiveConditionalNested : WithDistributiveConditionalNested
102+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
103+
104+
arg: T extends unknown ? { animal: T } : never,
105+
>arg : T extends unknown ? { animal: T; } : never
106+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^
107+
>animal : T
108+
> : ^
109+
110+
) => void;
111+
112+
type Result2 = Parameters<WithDistributiveConditionalNested>;
113+
>Result2 : [arg: { animal: Cat; } | { animal: Dog; }]
114+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
115+
116+
type WithNonDistributiveConditionalNested = <T extends Cat | Dog>(
117+
>WithNonDistributiveConditionalNested : WithNonDistributiveConditionalNested
118+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
119+
120+
arg: [T] extends [unknown] ? { animal: T } : never,
121+
>arg : [T] extends [unknown] ? { animal: T; } : never
122+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^
123+
>animal : T
124+
> : ^
125+
126+
) => void;
127+
128+
type Result3 = Parameters<WithNonDistributiveConditionalNested>;
129+
>Result3 : [arg: { animal: Cat | Dog; }]
130+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
131+

tests/baselines/reference/typePredicateFreshLiteralWidening.symbols

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ const values1 = [item1, item2, item3].map(item => item.value);
8888
>item3 : Symbol(item3, Decl(typePredicateFreshLiteralWidening.ts, 19, 5))
8989
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
9090
>item : Symbol(item, Decl(typePredicateFreshLiteralWidening.ts, 24, 42))
91-
>item.value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 17, 33), Decl(typePredicateFreshLiteralWidening.ts, 18, 33), Decl(typePredicateFreshLiteralWidening.ts, 19, 33))
91+
>item.value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 15, 13))
9292
>item : Symbol(item, Decl(typePredicateFreshLiteralWidening.ts, 24, 42))
93-
>value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 17, 33), Decl(typePredicateFreshLiteralWidening.ts, 18, 33), Decl(typePredicateFreshLiteralWidening.ts, 19, 33))
93+
>value : Symbol(value, Decl(typePredicateFreshLiteralWidening.ts, 15, 13))
9494

9595
const filteredValues1 = values1.filter(isNotNull);
9696
>filteredValues1 : Symbol(filteredValues1, Decl(typePredicateFreshLiteralWidening.ts, 25, 5))

0 commit comments

Comments
 (0)