Search code examples
typescripttuplesrecursive-type

How to get a flattened tuple type of a tuple of tuples?


Consider I have a tuple of tuples:

type Example = [[3,5,7], [4,9], [0,1,10,9]];

I want to create an utility type Flatten<T> such that Flatten<Example> gives:

type FlatExample = Flatten<Example>;
// type FlatExample = [3,5,7,4,9,0,1,10,9];

For my use case, you can assume the tuple is nested only one level deep. The tuples can have any size.

How can I do this?


Solution

  • To do this you would require recursive conditional types. This will be fully supported in 4.1:

    type Example = [[3,5,7], [4,9], [0,1,10,9]];
    type Flatten<T extends any[]> = 
        T extends [infer U, ...infer R] ? U extends any[] ? [...U, ... Flatten<R>]: []: []
    type FlatExample = Flatten<Example>;
    

    Playground Link

    Edit

    Simpler version, curtesy of jcalz:

    type Example = [[3,5,7], [4,9], [0,1,10,9]];
    type Flatten<T extends any[]> = 
        T extends [any, ...infer R] ? [...T[0], ... Flatten<R>]:  []
    type FlatExample = Flatten<Example>;
    

    Playground Link

    /Edit

    You can hack a version even today (the extra indirection is needed to fool the compiler into allowing the recursive conditional type, conceptually this is equivalent to the simpler above version):

    type Example = [[3, 5, 7], [4, 9], [0, 1, 10, 9]];
    type Flatten<T extends any[]> = T extends [infer U, ...infer R] ? {
        1: U extends any[] ? [...U, ...Flatten<R>] : [],
        2: []
    }[U extends any[] ? 1 : 2] : [];
    
    type FlatExample = Flatten<Example>;
    
    

    Playground Link

    Just for fun, the 4.1, generalized flattening version.

    type Example = [[3,5,7], [4,9, [10, 12, [10, 12]]], [0,1,10,9, [10, 12]]];
    type Flatten<T extends any[]> = 
        T extends [infer U, ...infer R] ? 
            U extends any[] ? 
            [...Flatten<U>, ... Flatten<R>]: [U, ... Flatten<R>]: []
    type FlatExample = Flatten<Example>;
    

    Playground Link

    Note: While recursive types are more supported in 4.1, you can still run into compiler hardcoded limits, such as type instantiation depth, and total type instances (as such recursive types generate a lot of type instantiations). So use sparingly.