luky
luky

Reputation: 2370

Typescript convert type Tuple of tuple to Tuple (flatten Tuple)

I have this

Type T = Params<[Tuple1, Tuple2]> // eg [[string], [number]]

How to make of it (flatten)

Type Flatten<T> = Params<[string, number]> 

Upvotes: 6

Views: 2015

Answers (2)

xAlien95
xAlien95

Reputation: 406

You can now define recursive types:

type Flatten<T> = (
    T extends [] ? [] :
    T extends [infer T0] ? [...Flatten<T0>] :
    T extends [infer T0, ...infer Ts] ? [...Flatten<T0>, ...Flatten<Ts>] : [T]
)
type T = Flatten<[[number, number, []], string, [[]], [string, [number, string]]]>
// T is [number, number, string, string, number, string]

Playground.

Upvotes: 3

Maciej Sikora
Maciej Sikora

Reputation: 20132

We can do that by mapped types. Consider the code:

type T = Params<[Tuple1, Tuple2]>

// utility type
type Flatten<T extends any[]> = {
  [K in keyof T]: T[K] extends any[] ? T[K][0] : T[K]
}
type Result = Flatten<T>
// evaluates into [string, number]

const a: Result = ['a', 1] // correct value of the type

Pay attention how Flatten works:

  • [K in keyof T] - means we want to have all keys, so in tuple say elements
  • T[K] extends any[] ? T[K][0] : T[K] - we say if the value of the element on the given key is an array then give me a type of an first element of this array (index 0), if not leave it as is, as there is nothing to be flatten

If your tuples consider more then one type of the element, then above solution will take the first only. So for tuple [string, number] it will produce the string. If we want to gather all possible types inside the tuple we can create more sophisticated type. Consider:

type Flatten<T extends any[]> = {
  [K in keyof T]: T[K] extends any[] ? T[K][Exclude<keyof T[K], keyof any[]>] : T[K]
}
  • T[K] extends any[] ? T[K][Exclude<keyof T[K], keyof any[]>] : T[K] means that if our element is an array then get me types of all elements, but remove types of values in array prototype.

In the result Flatten<Params<[string, number]>> will produce [string | number]. So it depends what is your goal.


The last proposition is, if you don't consider other types and only nested array/tuples, we can avoid conditional types. Consider last solution:

type Flatten<T extends E[], E extends any[] = any[]> = {
  [K in keyof T]: T[K][Exclude<keyof T[K], keyof any[]>]
}

Above type is more restrict as it works with only [[]] but is more concise and specific.

Upvotes: 4

Related Questions