Skip to content

Recursive conditional types #40002

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Aug 16, 2020
185 changes: 78 additions & 107 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5292,8 +5292,6 @@ namespace ts {
node: ConditionalTypeNode;
checkType: Type;
extendsType: Type;
trueType: Type;
falseType: Type;
isDistributive: boolean;
inferTypeParameters?: TypeParameter[];
outerTypeParameters?: TypeParameter[];
Expand Down
2 changes: 0 additions & 2 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2605,8 +2605,6 @@ declare namespace ts {
node: ConditionalTypeNode;
checkType: Type;
extendsType: Type;
trueType: Type;
falseType: Type;
isDistributive: boolean;
inferTypeParameters?: TypeParameter[];
outerTypeParameters?: TypeParameter[];
Expand Down
2 changes: 0 additions & 2 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2605,8 +2605,6 @@ declare namespace ts {
node: ConditionalTypeNode;
checkType: Type;
extendsType: Type;
trueType: Type;
falseType: Type;
isDistributive: boolean;
inferTypeParameters?: TypeParameter[];
outerTypeParameters?: TypeParameter[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,5 @@ type T0 = Foo<99>;
>T0 : "99"

type T1 = Foo<any>;
>T1 : "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10" | "11" | "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19" | "20" | "21" | "22" | "23" | "24" | "25" | "26" | "27" | "28" | "29" | "30" | "31" | "32" | "33" | "34" | "35" | "36" | "37" | "38" | "39" | "40" | "41" | "42" | "43" | "44" | "45" | "46" | "47" | "48" | "49" | "50" | "51" | "52" | "53" | "54" | "55" | "56" | "57" | "58" | "59" | "60" | "61" | "62" | "63" | "64" | "65" | "66" | "67" | "68" | "69" | "70" | "71" | "72" | "73" | "74" | "75" | "76" | "77" | "78" | "79" | "80" | "81" | "82" | "83" | "84" | "85" | "86" | "87" | "88" | "89" | "90" | "91" | "92" | "93" | "94" | "95" | "96" | "97" | "98" | "99"
>T1 : "99" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "10" | "11" | "12" | "13" | "14" | "15" | "16" | "17" | "18" | "19" | "20" | "21" | "22" | "23" | "24" | "25" | "26" | "27" | "28" | "29" | "30" | "31" | "32" | "33" | "34" | "35" | "36" | "37" | "38" | "39" | "40" | "41" | "42" | "43" | "44" | "45" | "46" | "47" | "48" | "49" | "50" | "51" | "52" | "53" | "54" | "55" | "56" | "57" | "58" | "59" | "60" | "61" | "62" | "63" | "64" | "65" | "66" | "67" | "68" | "69" | "70" | "71" | "72" | "73" | "74" | "75" | "76" | "77" | "78" | "79" | "80" | "81" | "82" | "83" | "84" | "85" | "86" | "87" | "88" | "89" | "90" | "91" | "92" | "93" | "94" | "95" | "96" | "97" | "98"

4 changes: 2 additions & 2 deletions tests/baselines/reference/promiseTypeInference.types
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ declare function convert(s: string): IPromise<number>;
>s : string

var $$x = load("something").then(s => convert(s));
>$$x : CPromise<unknown>
>load("something").then(s => convert(s)) : CPromise<unknown>
>$$x : CPromise<number>
>load("something").then(s => convert(s)) : CPromise<number>
>load("something").then : <U>(success?: (value: string) => CPromise<U>) => CPromise<U>
>load("something") : CPromise<string>
>load : (name: string) => CPromise<string>
Expand Down
195 changes: 195 additions & 0 deletions tests/baselines/reference/recursiveConditionalTypes.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
tests/cases/compiler/recursiveConditionalTypes.ts(16,11): error TS2589: Type instantiation is excessively deep and possibly infinite.
tests/cases/compiler/recursiveConditionalTypes.ts(20,5): error TS2322: Type 'Awaited<T>' is not assignable to type 'Awaited<U>'.
Type 'T' is not assignable to type 'U'.
'U' could be instantiated with an arbitrary type which could be unrelated to 'T'.
tests/cases/compiler/recursiveConditionalTypes.ts(21,5): error TS2322: Type 'T' is not assignable to type 'Awaited<T>'.
tests/cases/compiler/recursiveConditionalTypes.ts(22,5): error TS2322: Type 'Awaited<T>' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'Awaited<T>'.
Type 'T | (T extends PromiseLike<infer U> ? Awaited<U> : T)' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'T | (T extends PromiseLike<infer U> ? Awaited<U> : T)'.
Type 'T extends PromiseLike<infer U> ? Awaited<U> : T' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'T extends PromiseLike<infer U> ? Awaited<U> : T'.
Type 'unknown' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'unknown'.
tests/cases/compiler/recursiveConditionalTypes.ts(35,11): error TS2589: Type instantiation is excessively deep and possibly infinite.
tests/cases/compiler/recursiveConditionalTypes.ts(46,12): error TS2589: Type instantiation is excessively deep and possibly infinite.
tests/cases/compiler/recursiveConditionalTypes.ts(49,5): error TS2322: Type 'TupleOf<number, M>' is not assignable to type 'TupleOf<number, N>'.
Type 'number extends M ? number[] : _TupleOf<number, M, []>' is not assignable to type 'TupleOf<number, N>'.
Type 'number[] | _TupleOf<number, M, []>' is not assignable to type 'TupleOf<number, N>'.
Type 'number[]' is not assignable to type 'TupleOf<number, N>'.
tests/cases/compiler/recursiveConditionalTypes.ts(50,5): error TS2322: Type 'TupleOf<number, N>' is not assignable to type 'TupleOf<number, M>'.
Type 'number extends N ? number[] : _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
Type 'number[] | _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
Type 'number[]' is not assignable to type 'TupleOf<number, M>'.
tests/cases/compiler/recursiveConditionalTypes.ts(116,5): error TS2589: Type instantiation is excessively deep and possibly infinite.
tests/cases/compiler/recursiveConditionalTypes.ts(116,9): error TS2345: Argument of type 'Grow2<[], T>' is not assignable to parameter of type 'Grow1<[], T>'.
Type '[] | Grow2<[string], T>' is not assignable to type 'Grow1<[], T>'.
Type '[]' is not assignable to type 'Grow1<[], T>'.
Type 'Grow2<[string], T>' is not assignable to type 'Grow1<[number], T>'.
Type '[string] | Grow2<[string, string], T>' is not assignable to type 'Grow1<[number], T>'.
Type '[string]' is not assignable to type 'Grow1<[number], T>'.
Type '[string]' is not assignable to type '[number]'.
Type 'string' is not assignable to type 'number'.


==== tests/cases/compiler/recursiveConditionalTypes.ts (10 errors) ====
// Awaiting promises

type Awaited<T> =
T extends null | undefined ? T :
T extends PromiseLike<infer U> ? Awaited<U> :
T;

type MyPromise<T> = {
then<U>(f: ((value: T) => U | PromiseLike<U>) | null | undefined): MyPromise<U>;
}

type InfinitePromise<T> = Promise<InfinitePromise<T>>;

type P0 = Awaited<Promise<string | Promise<MyPromise<number> | null> | undefined>>;
type P1 = Awaited<any>;
type P2 = Awaited<InfinitePromise<number>>; // Error
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.

function f11<T, U extends T>(tx: T, ta: Awaited<T>, ux: U, ua: Awaited<U>) {
ta = ua;
ua = ta; // Error
~~
!!! error TS2322: Type 'Awaited<T>' is not assignable to type 'Awaited<U>'.
!!! error TS2322: Type 'T' is not assignable to type 'U'.
!!! error TS2322: 'U' could be instantiated with an arbitrary type which could be unrelated to 'T'.
ta = tx; // Error
~~
!!! error TS2322: Type 'T' is not assignable to type 'Awaited<T>'.
tx = ta; // Error
~~
!!! error TS2322: Type 'Awaited<T>' is not assignable to type 'T'.
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'Awaited<T>'.
!!! error TS2322: Type 'T | (T extends PromiseLike<infer U> ? Awaited<U> : T)' is not assignable to type 'T'.
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'T | (T extends PromiseLike<infer U> ? Awaited<U> : T)'.
!!! error TS2322: Type 'T extends PromiseLike<infer U> ? Awaited<U> : T' is not assignable to type 'T'.
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'T extends PromiseLike<infer U> ? Awaited<U> : T'.
!!! error TS2322: Type 'unknown' is not assignable to type 'T'.
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'unknown'.
}

// Flattening arrays

type Flatten<T extends readonly unknown[]> = T extends unknown[] ? _Flatten<T>[] : readonly _Flatten<T>[];
type _Flatten<T> = T extends readonly (infer U)[] ? _Flatten<U> : T;

type InfiniteArray<T> = InfiniteArray<T>[];

type B0 = Flatten<string[][][]>;
type B1 = Flatten<string[][] | readonly (number[] | boolean[][])[]>;
type B2 = Flatten<InfiniteArray<string>>;
type B3 = B2[0]; // Error
~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.

// Repeating tuples

type TupleOf<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
type _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N ? R : _TupleOf<T, N, [T, ...R]>;

type TT0 = TupleOf<string, 4>;
type TT1 = TupleOf<number, 0 | 2 | 4>;
type TT2 = TupleOf<number, number>;
type TT3 = TupleOf<number, any>;
type TT4 = TupleOf<number, 100>; // Depth error
~~~~~~~~~~~~~~~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.

function f22<N extends number, M extends N>(tn: TupleOf<number, N>, tm: TupleOf<number, M>) {
tn = tm;
~~
!!! error TS2322: Type 'TupleOf<number, M>' is not assignable to type 'TupleOf<number, N>'.
!!! error TS2322: Type 'number extends M ? number[] : _TupleOf<number, M, []>' is not assignable to type 'TupleOf<number, N>'.
!!! error TS2322: Type 'number[] | _TupleOf<number, M, []>' is not assignable to type 'TupleOf<number, N>'.
!!! error TS2322: Type 'number[]' is not assignable to type 'TupleOf<number, N>'.
tm = tn;
~~
!!! error TS2322: Type 'TupleOf<number, N>' is not assignable to type 'TupleOf<number, M>'.
!!! error TS2322: Type 'number extends N ? number[] : _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
!!! error TS2322: Type 'number[] | _TupleOf<number, N, []>' is not assignable to type 'TupleOf<number, M>'.
!!! error TS2322: Type 'number[]' is not assignable to type 'TupleOf<number, M>'.
}

declare function f23<T>(t: TupleOf<T, 3>): T;

f23(['a', 'b', 'c']); // string

// Inference to recursive type

interface Box<T> { value: T };
type RecBox<T> = T | Box<RecBox<T>>;
type InfBox<T> = Box<InfBox<T>>;

declare function unbox<T>(box: RecBox<T>): T

type T1 = Box<string>;
type T2 = Box<T1>;
type T3 = Box<T2>;
type T4 = Box<T3>;
type T5 = Box<T4>;
type T6 = Box<T5>;

declare let b1: Box<Box<Box<Box<Box<Box<string>>>>>>;
declare let b2: T6;
declare let b3: InfBox<string>;
declare let b4: { value: { value: { value: typeof b4 }}};

unbox(b1); // string
unbox(b2); // string
unbox(b3); // InfBox<string>
unbox({ value: { value: { value: { value: { value: { value: 5 }}}}}}); // number
unbox(b4); // { value: { value: typeof b4 }}
unbox({ value: { value: { get value() { return this; } }}}); // { readonly value: ... }

// Inference from nested instantiations of same generic types

type Box1<T> = { value: T };
type Box2<T> = { value: T };

declare function foo<T>(x: Box1<Box1<T>>): T;

declare let z: Box2<Box2<string>>;

foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference)

// Intersect tuple element types

type Intersect<U extends any[], R = unknown> = U extends [infer H, ...infer T] ? Intersect<T, R & H> : R;

type QQ = Intersect<[string[], number[], 7]>;

// Infer between structurally identical recursive conditional types

type Unpack1<T> = T extends (infer U)[] ? Unpack1<U> : T;
type Unpack2<T> = T extends (infer U)[] ? Unpack2<U> : T;

function f20<T, U extends T>(x: Unpack1<T>, y: Unpack2<T>) {
x = y;
y = x;
f20(y, x);
}

type Grow1<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow1<[number, ...T], N>;
type Grow2<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow2<[string, ...T], N>;

function f21<T extends number>(x: Grow1<[], T>, y: Grow2<[], T>) {
f21(y, x); // Error
~~~~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
~
!!! error TS2345: Argument of type 'Grow2<[], T>' is not assignable to parameter of type 'Grow1<[], T>'.
!!! error TS2345: Type '[] | Grow2<[string], T>' is not assignable to type 'Grow1<[], T>'.
!!! error TS2345: Type '[]' is not assignable to type 'Grow1<[], T>'.
!!! error TS2345: Type 'Grow2<[string], T>' is not assignable to type 'Grow1<[number], T>'.
!!! error TS2345: Type '[string] | Grow2<[string, string], T>' is not assignable to type 'Grow1<[number], T>'.
!!! error TS2345: Type '[string]' is not assignable to type 'Grow1<[number], T>'.
!!! error TS2345: Type '[string]' is not assignable to type '[number]'.
!!! error TS2345: Type 'string' is not assignable to type 'number'.
}

Loading