Repository navigation
Description
Disclaimer: This is probably not intended use of the type system but it might show an underlying issue so I still report it.
TypeScript Version: 3.1.0-dev.20180802
Search Terms: recursive conditional type tuple freeze
Code
While experimenting with the examples found in #24897 (comment), I noticed some weird behavior when it comes to recursive types. Apparently tail recursion on type members seems to work, which means it can be used to create crazy types like Reverse:
/**
* Returns the first item's type in a tuple
*/
export type Head<T extends any[]> =
T extends [infer H, ...any[]] ? H : never;
/**
* Returns all but the first item's type in a tuple/array
*/
export type Tail<T extends any[]> =
((...args: T) => any) extends ((head: any, ...tail: infer R) => any) ? R : never;
/**
* Returns the given tuple/array with the item type prepended to it
*/
export type Unshift<List extends any[], Item> =
((first: Item, ...rest: List) => any) extends ((...list: infer R) => any) ? R : never;
/** Reverses the given list */
export type Reverse<List extends any[]> = _Reverse<List, []>;
export type _Reverse<Source extends any[], Result extends any[] = []> = {
// If the source list is empty, return the result
1: Result,
// else prepend the head of source to the result list and
// continue recursion with the tail of the source
0: _Reverse<Tail<Source>, Unshift<Result, Head<Source>>>,
}[Source extends [] ? 1 : 0];When opening a file where that type is used, VSCode's Intellisense shows the correct type after a minor delay:
type Foo = Reverse<[1, 2, 3]>; // [3, 2, 1]
const f: Foo = [2, 1]; // error: 2 is not assignable to 3After adding a comma after the 3 in the recursive type, TSServers comes to a halt (1 core with full CPU usage).
type Foo = Reverse<[1, 2, 3,]>; // FREEZEHowever, if I paste a comma and a number from somewhere else in a single operation, it works just fine:
type Foo = Reverse<[1, 2, 3,4]>; // ",4" pasted from somewhere => all good!The same thing happens when changing a type to this recursive type
// start here
type Foo = [1, 2, 3];
// change to this:
type Foo = Reverse<[1, 2, 3]; // brokenbut not doing it in this order:
// start here
type Foo = [1, 2, 3];
// change to this:
type Foo = <[1, 2, 3]>;
// then to this:
type Foo = Reverse<[1, 2, 3]>; // all good!Related Issues: #24897
Activity
AlCalzone commentedon Aug 3, 2018
Potentially relevant: While the process hangs, its RAM usage continues to rise - as if its building some infinite signature help or something like that.
KiaraGrouwstra commentedon Aug 9, 2018
afaik TS did do stuff on trailing comma for Intellisense purposes... not sure what's happening here though.
AlCalzone commentedon Aug 24, 2018
I might have a clue whats going on behind the scenes. Using typescript@3.1.0-dev.20180824 and the following code:
Intellisense shows an error on the two lines using

Reverse<>:Apparently, a huge type is generated behind the scenes even without any parameters being provided
AlCalzone commentedon Aug 24, 2018
Update: Using typescript@3.1.0-dev.20180824, the original code no longer completely freezes the language server. It's still incredibly slow, but works.
Add test for signaure help with recursive type
AlCalzone commentedon Aug 31, 2018
Sheetal Nandi (@sheetalkamat) Ryan Cavanaugh (@RyanCavanaugh)
Do you consider this as fixed? If so, what about the HUGE type generated in my last example? That does not seem intended. Should I open another issue for that?
sheetalkamat commentedon Aug 31, 2018
with typescript@next there is no huge type generated that I can repro with your example.
AlCalzone commentedon Sep 1, 2018
That seems to be because

ConcatReverseis nownever(using today's typescript@next).When replacing the now no longer functional version of that type with a naive implementation that just loops the tuple, there is still a large type generated without any passed type arguments. Although it is not as huge as it used to be:
