ยท new features
How to use Mapped Types in TypeScript
TypeScript 2.1 introduced mapped types, which allow you to create new types based on the properties of an existing type. For example, you can create a mapped type that has the same keys as an existing type, but with optional values.
TypeScript 2.1 introduced mapped types which allow you to build new types based on the properties of an existing type.
Contents
Mapped Types in Action
For example, you can create a mapped type that is the same keys as an existing type, except that all values are optional:
interface User {
age: number;
firstName: string;
lastName: string;
}
type PartialUser = {
[P in keyof User]?: User[P];
};
// The following assignments are allowed now
const user: PartialUser = {
age: 35,
};
The PartialUser
from the code listed above is called a mapped type because it defines a mapping of existing properties to optional properties.
Using Utility Types
TypeScript already provides so-called utility types such as Partial
or Required
to transform the modifiers of all properties of a given type. We can make use of this to minimize our code example:
interface User {
age: number;
firstName: string;
lastName: string;
}
type PartialUser = Partial<User>;
// The following assignments are allowed now
const user: PartialUser = {
age: 35,
};
Mapped Type Modifiers
In TypeScript 2.8 property modifiers have been added to extend the capabilities of mapped types. In addition to the optional modifier (?
), a removing (-
) and adding (+
) have been introduced. This makes it possible to remove or add a previously added modifier.
Example
interface User {
age: number;
firstName: string;
lastName: string;
}
type PartialUser = {
[P in keyof User]?: User[P];
};
type NonPartialUser = {
[P in keyof PartialUser]-?: PartialUser[P];
};
// TS2739: Type '{ age: number; }' is missing the following properties from type 'NonPartialUser': firstName, lastName
const user: NonPartialUser = {
age: 35,
};
Advanced Mapping
It is possible to use type assertions in order to change property names of types completely.
Example
The following code makes use of the built-in Template Literal Type Capitalize
to change the property names of UserValidation
:
interface User {
age: number;
firstName: string;
lastName: string;
}
type UserValidation = {
[P in keyof User as `has${Capitalize<P>}`]: boolean;
};
The mapped UserValidation
type is equivalent to:
type UserValidation = {
hasAge: boolean;
hasFirstName: boolean;
hasLastName: boolean;
};