Search code examples
typescripttypescript3.0

How to 'map' a Tuple to another Tuple type in Typescript 3.0


I have tuple of Maybe types:

class Maybe<T>{ }

type MaybeTuple = [Maybe<string>, Maybe<number>, Maybe<boolean>];

and I want to turn this into a tuple of types:

type TupleIWant = [string, number, boolean];

so I tried this:

type ExtractTypes<T> = T extends Maybe<infer MaybeTypes>[] ? MaybeTypes : never;

type TypesArray = ExtractTypes<MaybeTuple>; // string | number | boolean NOT [string, number, boolean]

Which doesn't work :-(

I get (string | number | boolean)[] rather than the tuple I want: [string, number, boolean]

Is what I want to do currently possible?


Solution

  • You'll need to use a mapped tuple type, which is supported in TypeScript 3.1.

    You can make a mapped type that has properties 0, 1, 2 and length of the correct types, like this:

    class Maybe<T> {}
    
    type MaybeTuple = [Maybe<string>, Maybe<number>, Maybe<boolean>];
    
    type MaybeType<T> = T extends Maybe<infer MaybeType> ? MaybeType : never;
    type MaybeTypes<Tuple extends [...any[]]> = {
      [Index in keyof Tuple]: MaybeType<Tuple[Index]>;
    } & {length: Tuple['length']};
    
    let extractedTypes: MaybeTypes<MaybeTuple> = ['hello', 3, true];
    

    If you're using an older version of typescript, you can use an unreleased version of TypeScript, or as a workaround, you can write a conditional type to match against tuples as long as you think you are likely to have, e.g., like this.