ยท 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:

mapped-type.ts
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:

mapped-type.ts
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;
};
Back to Blog