how to write an `Invert` type in typescript to invert the order of tuples
Asked Answered
T

3

8
type a = [1,2,3]
type Invert<T extends any[] & {'0': any}> = ???
type b = Invert<a> // should yield [3,2,1]

I am stucked to figure out the definition of Invert type of a tuple, also an Init and Last type, although they may be constructed of each others

what I have tried:

  1. position the type in a function param definition and infer the Rest part, this approach only got the Tail part with rest params
Triplenerved answered 9/1, 2020 at 6:47 Comment(0)
T
5

as of typescript 4.0 code to approach a Reverse type is much easier than before, saying

type Reverse<T extends any[], R extends any[] = []> =  ReturnType<T extends [infer F, ...infer L] ? () => Reverse<L,[F,...R]> : () => R>

some explanation:

  1. direct reference type Reverse in typealias Reverse will result in circularly references error.
  2. wrap Reverse type in function type (()=> Reverse) will opt-out the circularly references error
  3. if type expression could resolve staticly, the compiler will try to resolve it, so ReturnType<() => Tup<L,[F,...R]>> wont do the trick, but ReturnType<ConditionalType> will do
Triplenerved answered 9/8, 2020 at 13:23 Comment(1)
You are a genius! Thanks!Nomarch
B
12

TypeScript 4.1 introduced recursive conditional types.

They allow a tuple reversal function to be implemented like this:

type Reverse<Tuple> = Tuple extends [infer Head, ...infer Rest]
       ? [...Reverse<Rest>, Head] : [];

const test: Reverse<[1,2,3,4]> = [4,3,2,1];
Befoul answered 24/11, 2020 at 20:12 Comment(1)
Thank you for this answer. I had not thought of just spreading the result of the recursive call. Fantastic idea and I will definitely use this approach more often.Rainy
T
5

as of typescript 4.0 code to approach a Reverse type is much easier than before, saying

type Reverse<T extends any[], R extends any[] = []> =  ReturnType<T extends [infer F, ...infer L] ? () => Reverse<L,[F,...R]> : () => R>

some explanation:

  1. direct reference type Reverse in typealias Reverse will result in circularly references error.
  2. wrap Reverse type in function type (()=> Reverse) will opt-out the circularly references error
  3. if type expression could resolve staticly, the compiler will try to resolve it, so ReturnType<() => Tup<L,[F,...R]>> wont do the trick, but ReturnType<ConditionalType> will do
Triplenerved answered 9/8, 2020 at 13:23 Comment(1)
You are a genius! Thanks!Nomarch
M
2

This only works when you know the length of the array:

type a = [1,2,3]
type Invert<T extends [any, any, any]> = [T[2], T[1], T[0]];
type b = Invert<a> // should yield [3,2,1]

Update

Actually there exists a solution (found here at an issue of the typescript project):

export type Prepend<Tuple extends any[], Addend> = 
     ((_: Addend, ..._1: Tuple) => any) extends ((..._: infer Result) => any) ? Result : never;
    
export type Reverse<Tuple extends any[], Prefix extends any[] = []> = {
    0: Prefix;
    1: ((..._: Tuple) => any) extends ((_: infer First, ..._1: infer Next) => any)
        ? Reverse<Next, Prepend<Prefix, First>>
        : never;
}[Tuple extends [any, ...any[]] ? 1 : 0];


type b = Reverse<[1, 2, 3]>; // type b = [3, 2, 1]

Playground Link

Myotome answered 9/1, 2020 at 8:34 Comment(4)
actually, i want to get a generic versionUnlimber
If you mean a type definition that reverses any number of array entries, this is not possible. As you mentioned above, this requires a head and tail operator, which in turn requires a rest operator in combination with a recursion for generic constrains. Both do not exist for generics.Myotome
@Minami My last comment isn't true, there exists a generic version. See my updated answer.Myotome
@1y1nq nice solutionUnlimber

© 2022 - 2024 — McMap. All rights reserved.