Mapped types in TypeScript allow you to transform an existing type into a new type by mapping over its properties. This can be useful for creating new types that are based on existing types, but with some modifications.
They can also help avoid repeating yourself and make your types more flexible and reusable. Some use cases for mapped types are:
You can also use features like template literal types, conditional types, and generics to create more complex mapped types.
TypeScript provides some global utility types that are based on mapped types and can be used to manipulate existing types:
Partial<T>
: Makes all properties of T optional.Required<T>
: Makes all properties of T required.Readonly<T>
: Makes all properties of T readonly.Record<K, T>
: Creates a type with keys of type K and values of type T.Pick<T, K>
: Creates a type that picks a set of properties K from T.Omit<T, K>
: Creates a type that omits a set of properties K from T.Exclude<T, U>
: Creates a type that excludes all types that are assignable to U from T.Extract<T, U>
: Creates a type that extracts all types that are assignable to U from T.NonNullable<T>
: Creates a type that excludes null
and undefined
from T.Here are some examples of built-in mapped types:
type Person = {
name: string;
age: number;
};
type MaybePerson = Partial<Person>;
// Equivalent to:
// type MaybePerson = {
// name?: string;
// age?: number;
// };
type Animal = {
name: string;
species: string;
age: number;
};
type Pet = Pick<Animal, "name" | "age">;
// Equivalent to:
// type Pet = {
// name: string;
// age: number;
// };
Index signatures are a way to declare the types of properties that have not been declared ahead of time.
type IndexType = {
[key: KeyType]: ValueType;
};
For example, here is an index type that declares an object that can have any string keys and any values:
type AnyObject = {
[key: string]: any;
};
let obj: AnyObject = {
foo: 1,
bar: "hello",
baz: true,
};
Mapped types build on the syntax for index signatures by adding a way to iterate over the keys of another type and transform them into new keys and values. They use the following syntax:
type MappedType<Type> = {
[Property in keyof Type as NewProperty]: NewType;
};
Here, Property
is a type variable that iterates over the keys of Type
. NewProperty
is an expression that defines how to create new keys based on Property
. NewType
is an expression that defines how to create new values based on Type
and Property
.
readonly
and ?
to add or remove them from the properties. For example, here is a mapped type that takes a generic type Type
and creates a new type ReadonlyOptionalType
that has the same properties as Type
but with readonly and optional modifiers:type ReadonlyOptionalType<Type> = {
readonly [Property in keyof Type]?: Type[Property];
};
type Book = {
title: string;
author: string;
pages: number;
};
type BookInfo = ReadonlyOptionalType<Book>;
// Equivalent to:
// type BookInfo = {
// readonly title?: string;
// readonly author?: string;
// readonly pages?: number;
// };
as
keyword to change the names of the properties. For example, here is a mapped type that takes a generic type Type
and creates a new type SuffixType
that has the same properties as Type
but with keys suffixed by “_suffix”:type SuffixType<Type> = {
[Property in keyof Type as `${Property & string}_suffix`]: Type[Property];
};
type Animal = {
name: string;
species: string;
age: number;
};
type AnimalSuffix = SuffixType<Animal>;
// Equivalent to:
// type AnimalSuffix = {
// name_suffix: string;
// species_suffix: string;
// age_suffix: number;
// };
Type
and creates a new type NumberType
that has only the properties of Type
whose values are numbers:type NumberType<Type> = {
[Property in keyof Type]: Type[Property] extends number ? Type[Property] : never;
};
type Person = {
name: string;
age: number;
height: number;
};
type PersonNumbers = NumberType<Person>;
// Equivalent to:
// type PersonNumbers = {
// name: never;
// age: number;
// height: number;
// };
// TypeScript v4.5.5
type ReplaceType<Type, Value> = {
[Property in keyof Type]: Value;
};
type Book = {
title: string;
author: string;
pages: number;
};
type BookStatus = ReplaceType<Book, boolean>;
// Equivalent to:
// type BookStatus = {
// title: boolean;
// author: boolean;
// pages: boolean;
// };
type PrefixType<Type, Prefix extends string> = {
[Property in keyof Type as `${Prefix}${Property & string}`]: Type[Property];
};
type User = {
name: string;
email: string;
password: string;
};
type UserForm = PrefixType<User, "form_">;
// Equivalent to:
// type UserForm = {
// form_name: string;
// form_email: string;
// form_password: string;
// };