Menu

TypeScript Mapped Types: Syntax, Modifiers and Key Remapping

A mapped type builds a new object type by looping over keys: { [K in keyof T]: ... }. Learn the syntax, the readonly and ? modifiers with + and -, key remapping with as, filtering keys, and how Partial, Readonly, Required, Pick and Record are written.

This page includes runnable editors - edit, run, and see output instantly.

A mapped type builds a new object type by looping over a set of keys. { [K in keyof T]: boolean } means "for every key K of T, a property named K of type boolean":

Add a key to Features and Flags<Features> requires it too. Mapped types are compile-time only: they describe objects, they do not create them.

The Syntax

A mapped type has three parts: a key variable, a union of keys to iterate over, and the property type, which may use the key variable.

type MappedType = {
  [K in Keys]: PropertyType; // K takes each member of Keys in turn
};

Keys can be any union of strings, numbers or symbols. It does not have to come from keyof:

This is exactly what Record<Size, number> does; Record is a one-line mapped type. The property type can use the key: { [K in keyof T]: T[K] } copies each property type through an indexed access, and { [K in keyof T]: T[K] | null } makes every property nullable.

Modifiers: readonly and ? with + and -

A mapped type can add or remove the readonly and optional (?) modifiers on every property at once. Prefix + to add (the default when you write none) and - to remove:

The last line shows that readonly is only a compile-time rule: the assignment was reported (and suppressed here with @ts-expect-error), but the emitted JavaScript still ran it. -? does more than drop the question mark: it also removes undefined from the property type, so AllRequired<Account> rejects { id: 1, email: undefined } with error TS2322: Type 'undefined' is not assignable to type 'string'.

How Partial, Readonly, Pick and Record Are Built

The built-in utility types that reshape objects are mapped types. These are the definitions from TypeScript's own lib.es5.d.ts:

type Partial<T> = { [P in keyof T]?: T[P] };
type Required<T> = { [P in keyof T]-?: T[P] };
type Readonly<T> = { readonly [P in keyof T]: T[P] };
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
type Record<K extends keyof any, T> = { [P in K]: T };

Reading them is good practice: Pick iterates over only the keys you pass, and Record ignores any source type and gives every key the same value type. Omit is not a mapped type of its own: it is Pick<T, Exclude<keyof T, K>>. The utility types reference lists them all.

Key Remapping with as

An as clause after the key changes the property name. Combined with template literal types, it can generate new names from old ones:

string & K is there because keyof T may include number and symbol keys, and Capitalize only accepts strings. The intersection keeps the string keys and drops the rest.

Filtering Keys with never

If the as clause produces never for a key, that key is removed. With a conditional type you can keep or drop properties based on their type:

Types like DataOnly are useful for describing what survives JSON.stringify or what a form edits: the fields without the methods.

Which Modifiers Are Kept

A mapped type over keyof T for some type T is called homomorphic, and it copies each property's readonly and ? modifiers from T. A mapped type over a plain union of keys starts with no modifiers:

type Account = { readonly id: number; email?: string };

type Copy<T> = { [K in keyof T]: T[K] };
type A = Copy<Account>;
// { readonly id: number; email?: string }  (modifiers kept)

type B = { [K in "id" | "email"]: Account[K] };
// { id: number; email: string | undefined }  (email is required now)

B still has undefined in the email type, because Account["email"] includes it, but the property itself is no longer optional: { id: 1 } is rejected. This is why Partial<T> and friends map over keyof T: they keep everything they do not explicitly change.

A generic homomorphic mapped type applied to an array or tuple produces an array or tuple, not an object with numeric keys. Readonly<string[]> is readonly string[], and with type Box<T> = { [K in keyof T]: { value: T[K] } }, Box<[string, number]> is [{ value: string }, { value: number }].

Mapped Types and Runtime Code

Because a mapped type only describes an object, a function that builds such an object needs its own runtime loop. The type then describes its result:

The callbacks get their parameter types from the mapped type (v is string in one and number in the other) without any annotation. If you came here looking for the Map class rather than type mapping, see Map in TypeScript.

Frequently Asked Questions

What is a mapped type in TypeScript?

A type that creates an object type by iterating over a union of keys: { [K in Keys]: SomeType }. Most often the keys are keyof T, so the new type has the same keys as T with transformed property types, as in type Flags<T> = { [K in keyof T]: boolean }.

What do +readonly, -readonly, +? and -? mean in a mapped type?

They add or remove modifiers. readonly or +readonly makes every property readonly, -readonly removes readonly. ? or +? makes every property optional, -? makes them required and also removes undefined from their types. Required<T> is written with -?.

How do I rename keys in a mapped type?

Use an as clause after the key: { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] } turns name into getName. Mapping a key to never in the as clause removes it, which is how you filter properties.

Is a mapped type the same as Map in TypeScript?

No. A mapped type is a compile-time type transformation and produces no code. Map<K, V> is the JavaScript Map class, a runtime collection of key-value pairs. Searches for "typescript map type" often mean one or the other.

How are Partial and Readonly implemented?

As mapped types in the standard library: type Partial<T> = { [P in keyof T]?: T[P] } and type Readonly<T> = { readonly [P in keyof T]: T[P] }. Pick and Record are mapped types too; Omit is Pick combined with Exclude.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED