Skip to Content
Map Over Array Types In TypeScript

Map Over Array Types In TypeScript

Most TypeScript developers have heard of Mapped Types.

A mapped type is a generic type that uses a union of property keys to create a new type:

// turn any property into a boolean type OptionsFlags<Type> = { [Property in keyof Type]: boolean; }; type New = OptionsFlags<{a: string}> // {a: boolean}

This is similar to a for-in loop in JavaScript but uses types instead of values. I recently had to do the same for an array-based type and struggled to find a way.

Map over an array based type

Let’s look at the following example of a tuple type.

💡
Tip

Quick reminder: A tuple is a typed array with a pre-defined length and types for each index.

// define our tuple let ourTuple: [number, boolean, string];
const ids = ["a", "b"] as const; // We want to "loop" over each member of the tuple and create a type like this: // [{name: "a"}, {name: "b"}] <- This is a type not a value

You can try to come up with a solution yourself if you are curious.

Mapped types for arrays and toupels

Here is the solution for the previous problem.

// this works const ids = ["a", "b"] as const; type MappedTuple<T extends readonly string[]> = { [P in keyof T]: T[P] extends string | number | symbol ? { name: T[P] } : never; }; type Desired = MappedTuple<typeof ids>; // readonly [{name: "a"}, {name: "b"}]

In this solution, we use the same syntax we previously used for mapped types but apply it to a tuple type.

Why this works

Figuring out why this works can be confusing. The documentation for mapped types does not mention arrays or tuples. So, why does this work?

What we would expect

In JavaScript, most things are objects. Arrays are objects, Maps are objects, Functions are objects, and so on.

If we use a mapped type on an array or tuple type we would expect all of the object properties like .length, 0, or .toString to be part of the mapping.

As we can see in the example above, this does not happen. We only get the tuple/array members.

What actually happens

In version 3.1 TypeScript added support for mapped types for arrays and tuples.

Since then, mapped object types over tuples and arrays produce new tuples/arrays, rather than creating a new type where members like .push(), .pop(), and .length are converted.

This applies to mapped types only. Creating a type from the object properties of a tuple or array will still include all object properties like .length, .map(), etc.

type Prettify<T> = { [K in keyof T]: T[K]; } & {}; const ids = ["a", "b"] as const; type AllKeys = Prettify<keyof typeof ids>; // List all keys -> Not just members
logo-dark
Add clay-like data enrichment to your application. Fast.
Last updated on