🔷 TypeScript Q29 / 101

What is mapped type?

AI-Powered Answer ✓ Answered

Mapped types in TypeScript provide a powerful way to transform existing types into new types by iterating over the properties of the original type. They are a generic type that makes new types by mapping over the properties of an old type.

What are Mapped Types?

Mapped types are a feature in TypeScript that allows you to create new types based on existing types by transforming each property of the original type. They are akin to 'for...in' loops for types, iterating over the properties of a union of string | number | symbol keys and creating a new type with modified properties.

The syntax for a mapped type typically involves a type parameter K that iterates over a union of PropertyKey (e.g., keyof T), and then uses this K to define the keys and values of the new type. This enables operations like making all properties optional, read-only, or changing their type.

typescript
type MyPartial<T> = { [P in keyof T]?: T[P]; };

interface User { 
  id: number; 
  name: string; 
  email?: string; 
}

type PartialUser = MyPartial<User>;
// Equivalent to:
// {
//   id?: number;
//   name?: string;
//   email?: string;
// }

In this example, MyPartial<T> takes an existing type T and creates a new type where every property P from T (obtained using keyof T) is made optional (?). TypeScript's built-in Partial<T> utility type is implemented using a similar mapped type.

Key Remapping

Since TypeScript 4.1, you can remap keys within a mapped type using an as clause. This allows you to create new property names based on the original property names, enabling more complex type transformations.

typescript
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

interface Person {
  name: string;
  age: number;
}

type PersonGetters = Getters<Person>;
/*
Equivalent to:
{
  getName: () => string;
  getAge: () => number;
}
*/

Mapped Type Modifiers

Mapped types can also apply or remove readonly and optional modifiers using + or - prefixes. By default, + is implicitly used.

  • +readonly (default): Makes properties read-only.
  • -readonly: Removes the read-only modifier.
  • +optional (default): Makes properties optional.
  • -optional: Makes properties required.
typescript
type MutableRequired<T> = {
  -readonly [P in keyof T]-?: T[P];
};

interface LockedUser { 
  readonly id: number; 
  name?: string; 
}

type UnlockedUser = MutableRequired<LockedUser>;
// Equivalent to:
// {
//   id: number;
//   name: string;
// }

Common Use Cases

  • Creating Partial<T> or Required<T> types: Making all properties optional or required.
  • Creating Readonly<T> or Mutable<T> types: Making all properties read-only or writable.
  • Picking or Omitting properties: While Pick and Omit are built-in, they are fundamentally based on mapped types.
  • Transforming property types: Changing the type of each property (e.g., T[P] to Promise<T[P]>).
  • Generating API payloads or DTOs: Creating specific data transfer objects from larger domain models.
  • Key remapping: Changing the names of properties, for example, to add prefixes/suffixes ('get' + K).

Mapped types are a cornerstone of advanced type manipulation in TypeScript, enabling developers to create highly flexible and expressive type utilities that adapt to changing data structures and enforce desired patterns.