Skip to content

Commit 18cd79e

Browse files
authored
Allow infer type variables to have constraints inferred (microsoft#32093)
* Allow `infer` type variables to have constraints infered and allow the breakdown of substitutes in simplifiable source inferences * Still skip conditional inference when `extends infer Q` so such a pattern still acts as a constraint size breaker
1 parent 08e6bc2 commit 18cd79e

9 files changed

+966
-2
lines changed

src/compiler/checker.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12342,6 +12342,13 @@ namespace ts {
1234212342
}
1234312343
}
1234412344

12345+
function unwrapSubstitution(type: Type): Type {
12346+
if (type.flags & TypeFlags.Substitution) {
12347+
return (type as SubstitutionType).substitute;
12348+
}
12349+
return type;
12350+
}
12351+
1234512352
// Transform an indexed access to a simpler form, if possible. Return the simpler form, or return
1234612353
// the type itself if no transformation is possible. The writing flag indicates that the type is
1234712354
// the target of an assignment.
@@ -12353,7 +12360,7 @@ namespace ts {
1235312360
type[cache] = circularConstraintType;
1235412361
// We recursively simplify the object type as it may in turn be an indexed access type. For example, with
1235512362
// '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type.
12356-
const objectType = getSimplifiedType(type.objectType, writing);
12363+
const objectType = unwrapSubstitution(getSimplifiedType(type.objectType, writing));
1235712364
const indexType = getSimplifiedType(type.indexType, writing);
1235812365
// T[A | B] -> T[A] | T[B] (reading)
1235912366
// T[A | B] -> T[A] & T[B] (writing)
@@ -12531,7 +12538,11 @@ namespace ts {
1253112538
let combinedMapper: TypeMapper | undefined;
1253212539
if (root.inferTypeParameters) {
1253312540
const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None);
12534-
if (!checkTypeInstantiable) {
12541+
// We skip inference of the possible `infer` types unles the `extendsType` _is_ an infer type
12542+
// if it was, it's trivial to say that extendsType = checkType, however such a pattern is used to
12543+
// "reset" the type being build up during constraint calculation and avoid making an apparently "infinite" constraint
12544+
// so in those cases we refain from performing inference and retain the uninfered type parameter
12545+
if (!checkTypeInstantiable || !some(root.inferTypeParameters, t => t === extendsType)) {
1253512546
// We don't want inferences from constraints as they may cause us to eagerly resolve the
1253612547
// conditional type instead of deferring resolution. Also, we always want strict function
1253712548
// types rules (i.e. proper contravariance) for inferences.
@@ -17863,6 +17874,12 @@ namespace ts {
1786317874
invokeOnce(source, target, inferFromObjectTypes);
1786417875
}
1786517876
}
17877+
if (source.flags & TypeFlags.Simplifiable) {
17878+
const simplified = getSimplifiedType(source, contravariant);
17879+
if (simplified !== source) {
17880+
inferFromTypes(simplified, target);
17881+
}
17882+
}
1786617883
}
1786717884

1786817885
function inferWithPriority(source: Type, target: Type, newPriority: InferencePriority) {
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//// [ramdaToolsNoInfinite.ts]
2+
// All the following types are explained here:
3+
// https://medium.freecodecamp.org/typescript-curry-ramda-types-f747e99744ab
4+
// https://github.com/pirix-gh/medium/blob/master/types-curry-ramda/src/index.ts
5+
declare namespace Tools {
6+
type Head<T extends any[]> =
7+
T extends [any, ...any[]]
8+
? T[0]
9+
: never;
10+
11+
type Tail<T extends any[]> =
12+
((...t: T) => any) extends ((_: any, ...tail: infer TT) => any)
13+
? TT
14+
: [];
15+
16+
type HasTail<T extends any[]> =
17+
T extends ([] | [any])
18+
? false
19+
: true;
20+
21+
type Last<T extends any[]> = {
22+
0: Last<Tail<T>>;
23+
1: Head<T>;
24+
}[
25+
HasTail<T> extends true
26+
? 0
27+
: 1
28+
];
29+
30+
type Length<T extends any[]> =
31+
T['length'];
32+
33+
type Prepend<E, T extends any[]> =
34+
((head: E, ...args: T) => any) extends ((...args: infer U) => any)
35+
? U
36+
: T;
37+
38+
type Drop<N extends number, T extends any[], I extends any[] = []> = {
39+
0: Drop<N, Tail<T>, Prepend<any, I>>;
40+
1: T;
41+
}[
42+
Length<I> extends N
43+
? 1
44+
: 0
45+
];
46+
47+
type Cast<X, Y> = X extends Y ? X : Y;
48+
49+
type Pos<I extends any[]> =
50+
Length<I>;
51+
52+
type Next<I extends any[]> =
53+
Prepend<any, I>;
54+
55+
type Prev<I extends any[]> =
56+
Tail<I>;
57+
58+
type Iterator<Index extends number = 0, From extends any[] = [], I extends any[] = []> = {
59+
0: Iterator<Index, Next<From>, Next<I>>;
60+
1: From;
61+
}[
62+
Pos<I> extends Index
63+
? 1
64+
: 0
65+
];
66+
67+
type Reverse<T extends any[], R extends any[] = [], I extends any[] = []> = {
68+
0: Reverse<T, Prepend<T[Pos<I>], R>, Next<I>>;
69+
1: R;
70+
}[
71+
Pos<I> extends Length<T>
72+
? 1
73+
: 0
74+
];
75+
76+
type Concat<T1 extends any[], T2 extends any[]> =
77+
Reverse<Reverse<T1> extends infer R ? Cast<R, any[]> : never, T2>;
78+
79+
type Append<E, T extends any[]> =
80+
Concat<T, [E]>;
81+
82+
type ValueOfRecord<R> = R extends Record<any, infer T> ? T : never;
83+
}
84+
85+
declare namespace R {
86+
export type Placeholder = { __placeholder: void };
87+
}
88+
89+
declare namespace Curry {
90+
type GapOf<T1 extends any[], T2 extends any[], TN extends any[], I extends any[]> =
91+
T1[Tools.Pos<I>] extends R.Placeholder
92+
? Tools.Append<T2[Tools.Pos<I>], TN>
93+
: TN;
94+
95+
interface GapsOfWorker<T1 extends any[], T2 extends any[], TN extends any[] = [], I extends any[] = []> {
96+
0: GapsOf<T1, T2, GapOf<T1, T2, TN, I> extends infer G ? Tools.Cast<G, any[]> : never, Tools.Next<I>>;
97+
1: Tools.Concat<TN, Tools.Drop<Tools.Pos<I>, T2> extends infer D ? Tools.Cast<D, any[]> : never>;
98+
}
99+
type GapsOf<T1 extends any[], T2 extends any[], TN extends any[] = [], I extends any[] = []> = GapsOfWorker<T1, T2, TN, I>[
100+
Tools.Pos<I> extends Tools.Length<T1>
101+
? 1
102+
: 0
103+
];
104+
105+
type PartialGaps<T extends any[]> = {
106+
[K in keyof T]?: T[K] | R.Placeholder
107+
};
108+
109+
type CleanedGaps<T extends any[]> = {
110+
[K in keyof T]: NonNullable<T[K]>
111+
};
112+
113+
type Gaps<T extends any[]> = CleanedGaps<PartialGaps<T>>;
114+
115+
type Curry<F extends ((...args: any) => any)> =
116+
<T extends any[]>(...args: Tools.Cast<Tools.Cast<T, Gaps<Parameters<F>>>, any[]>) =>
117+
GapsOf<T, Parameters<F>> extends [any, ...any[]]
118+
? Curry<(...args: GapsOf<T, Parameters<F>> extends infer G ? Tools.Cast<G, any[]> : never) => ReturnType<F>>
119+
: ReturnType<F>;
120+
}
121+
122+
123+
//// [ramdaToolsNoInfinite.js]

0 commit comments

Comments
 (0)