Mapped Types
Transform every property of a type at once — the engine behind Partial, Readonly, and friends.
A mapped type walks over the keys of an existing type and produces a new property for each. The shape is { [K in keyof T]: ... }.
type Optional<T> = { [K in keyof T]?: T[K] };
type Frozen<T> = { readonly [K in keyof T]: T[K] };Modifiers you can add or remove
Prefix with + or - to add or strip readonly and ?. -readonly makes a frozen type writable again; -? makes optional properties required — this is literally how Required<T> works.
type Mutable<T> = { -readonly [K in keyof T]: T[K] };
type Concrete<T> = { [K in keyof T]-?: T[K] };Because T[K] looks up each value type, you can also transform values — wrap them in Promise, make them nullable, and so on.
Example
When to use it
- A Nullable<T> mapped type adds | null to every property of an interface to represent database nullable columns.
- An EventHandlers<T> mapped type prefixes every key in T with on to generate a full set of event handler props.
- A Getters<T> mapped type transforms every property into a getter function type for a proxy or store implementation.
More examples
Build Partial from scratch
Re-implements Partial<T> using a mapped type that iterates every key K and marks it optional with ?.
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
interface User { id: number; name: string; email: string; }
const patch: MyPartial<User> = { name: 'Bob' }; // only name providedNullable mapped type
Maps every property of T to T[K] | null, useful for representing objects that come from nullable database columns.
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
interface Config { host: string; port: number; }
type NullableConfig = Nullable<Config>;
// { host: string | null; port: number | null }Readonly mapped type
Adds the readonly modifier to every key by prefixing the mapped key clause, reproducing Readonly<T>.
type MyReadonly<T> = {
readonly [K in keyof T]: T[K];
};
interface Point { x: number; y: number; }
const p: MyReadonly<Point> = { x: 1, y: 2 };
// p.x = 5; // Error: cannot assign to readonly property
Discussion