Mapped Types
Learn how to transform every property of an existing type into a new type using mapped type syntax.
Reading Time
18 min
Lesson
Lesson 23 of 30
What is a Mapped Type?
A mapped type builds a new type by iterating over the keys of an existing type (or a union of literal keys) and applying the same transformation to each one — it’s essentially a "map()" for types.
A Basic Mapped Type
type User = {
name: string;
email: string;
};
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
// Equivalent to: { readonly name: string; readonly email: string }This is How Partial and Readonly Are Built
TypeScript’s own built-in Partial<T> and Readonly<T> utility types are just mapped types under the hood.
Reimplementing Partial
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
type PartialUser = MyPartial<User>;
// { name?: string; email?: string }Modifiers: Adding and Removing ? and readonly
A - prefix removes a modifier instead of adding it, letting a mapped type strip optionality or readonly from an existing type.
Removing Modifiers
type PartialUser = { name?: string; email?: string };
// Strips '?' from every property, making them all required again
type RequiredAgain = {
[K in keyof PartialUser]-?: PartialUser[K];
};Remapping Keys with as
A mapped type can rename each key as it maps over them, using the as clause — useful for building things like a set of getter method names from a plain object type.
Key Remapping
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserGetters = Getters<User>;
// { getName: () => string; getEmail: () => string }Mapping Over a Union of Literals
Mapped types don’t need to start from keyof — they can iterate over any union of string literals directly.
Mapping Over a Literal Union
type Role = "admin" | "editor" | "viewer";
type RolePermissions = {
[R in Role]: boolean;
};
// { admin: boolean; editor: boolean; viewer: boolean }Best Practice
You rarely need to write mapped types from scratch — reach for the built-in utility types first, and write a custom mapped type only when nothing built-in expresses the exact transformation you need.