Iterate over Tuple Type in TypeScript without recursion

14 December 2024, 2 minutes

A simple trick to iterate over a tuple in TypeScript without resorting to recursion.

When creating a type derived from a tuple, the obvious method that comes to mind is through recursion i.e. something like this:

/**
 * Assuming the data here would be like
 * [ ["Name", "Description", "Age"], ... ]
**/
type DeserialisedList<T extends string[][]> = T extends
  [infer First extends string[], ...infer Rest extends string[][]]
  ? [DeserialisedRecord<First>, ...DeserialisedList<Rest>]
  : [];
type DeserialisedRecord<T extends string[]> = T extends
  [infer Name, infer Description, infer Age]
  ? { name: Name; description: Description; age: StringToNumber<Age> }
  : never;

This approach works fine for a record with a fewer entries. It can, however, become a problem as the size of the data increases.

TypeScript imposes recursion limits to prevent excessive compile-time computations, as deeply nested types or infinite recursion can significantly impact performance and lead to compiler crashes. These limits ensure that type-checking remains efficient and manageable.

To work around this we can use mapped types.

Mapped Types Are The Answer

Mapped types let you create an object with keys that are part of a union. The indices of a list correspond to the keys of an Array object. So we use that,

type FormatNames<T extends readonly [string, string, string][]> = {
  [key in keyof T]: DeserialisedRecord<T[key]>;
};

This approach works with a huge amount of data too, since there is no recursion at all.

The output of both the approaches looks identical for a lesser amount of data. This is what I used for my submission to Advent of TypeScript Day 12!

tech typescript ts mapped types recursion
END