Search Terms
circular type conditional type
Suggestion
Currently, Last1 is valid, but seemingly-equivalent Last2 is not:
typeHead<Textendsany[]>=Textends[infer X, ...any[]] ? X : never;typeTail<Textendsany[]>=((...x: T)=>void)extends((x: any, ...xs: infer XS)=>void) ? XS : never;typeLast1<Textendsany[]>={0: never,1: Head<T>,2: Last1<Tail<T>>,}[Textends[] ? 0 : Textends[any] ? 1 : 2];typeLast2<Textendsany[]>=Textends[] ? never :
Textends[infer R] ? R :
Last2<Tail<T>>;My suggestion is to make Last2 valid, since the recursion isn't necessarily unbounded.
Use Cases
It'd make recursive conditional types a lot easier and more intuitive to write. It'd also eliminate the need to guard against impossible cases when using the workaround like when using Last1 above. If you wanted to mandate termination, all I'd want is direct recursion - this makes it much easier to check for infinite recursion. (Other types are generally assumed to terminate, so you're only assessing control flow.)
Examples
See above in the suggestion summary. Here's a concrete example of how this file (with dependency) would be simplified:
typeLast<Lextendsany[],D=never>=Lextends[] ? D :
Lextends[infer H] ? H :
((...l: L)=>any)extends((h: any, ...t: infer T)=>any) ? Last<T> :
D;typeAppend<Textendsany[],H>=((h: H, ...t: T)=>any)extends((...l: infer L)=>any) ? L : never;typeReverse<Lextendsany[],Rextendsany[]=[]>=((...l: L)=>any)extends((h: infer H, ...t: infer T)=>any) ?
Reverse<T,Append<R,H>> :
R;typeCompose<Lextendsany[],V,Rextendsany[]=[]>=((...l: L)=>any)extends((a: infer H, ...t: infer T)=>any) ?
Compose<T,H,Append<R,(x: V)=>H>> :
R;exporttypePipeFunc<Textendsany[],V>=(...f: Reverse<Compose<T,V>>)=>((x: V)=>Last<T,V>);
Currently, you could achieve similar via this, but as you can see, it's highly repetitive and boilerplatey:
typeLast<Lextendsany[],D=never>={0: D,1: Lextends[infer H] ? H : never,2: ((...l: L)=>any)extends((h: any, ...t: infer T)=>any) ? Last<T> : D,}[Lextends[] ? 0 : Lextends[any] ? 1 : 2];typeAppend<Textendsany[],H>=((h: H, ...t: T)=>any)extends((...l: infer L)=>any) ? L : never;typeReverse<Lextendsany[],Rextendsany[]=[]>={0: R,1: ((...l: L)=>any)extends((h: infer H, ...t: infer T)=>any) ?
Reverse<T,Append<R,H>> :
never,}[Lextends[any, ...any[]] ? 1 : 0];typeCompose<Lextendsany[],V,Rextendsany[]=[]>={0: R,1: ((...l: L)=>any)extends((a: infer H, ...t: infer T)=>any) ?
Compose<T,H,Append<R,(x: V)=>H>>
: never,}[Lextends[any, ...any[]] ? 1 : 0];exporttypePipeFunc<Textendsany[],V>=(...f: Reverse<Compose<T,V>>)=>((x: V)=>Last<T,V>);(The original code snippet is linked to from here as a possible solution to the issue of _.compose, _.flow, and friends.)
Checklist
My suggestion meets these guidelines:
Search Terms
circular type conditional type
Suggestion
Currently,
Last1is valid, but seemingly-equivalentLast2is not:My suggestion is to make
Last2valid, since the recursion isn't necessarily unbounded.Use Cases
It'd make recursive conditional types a lot easier and more intuitive to write. It'd also eliminate the need to guard against impossible cases when using the workaround like when using
Last1above. If you wanted to mandate termination, all I'd want is direct recursion - this makes it much easier to check for infinite recursion. (Other types are generally assumed to terminate, so you're only assessing control flow.)Examples
See above in the suggestion summary. Here's a concrete example of how this file (with dependency) would be simplified:
Currently, you could achieve similar via this, but as you can see, it's highly repetitive and boilerplatey:
(The original code snippet is linked to from here as a possible solution to the issue of
_.compose,_.flow, and friends.)Checklist
My suggestion meets these guidelines: