Mapped Types

`{ [K in keyof T]: ... }` — Transform Every Property

Mapped Types

Mapped types create a new type by walking another type's keys. The foundation of `Partial`, `Readonly`, and the rest of the utility types.

5 min read Level 3/5 #typescript#mapped-types#advanced
What you'll learn
  • Read mapped-type syntax
  • Modify properties (optional, readonly)
  • Add or remove modifiers

A mapped type builds a new type by walking each key of another type and transforming the value.

The Syntax

type Stringify<T> = {
  [K in keyof T]: string;
};

type User = { name: string; age: number };
type StringyUser = Stringify<User>;
// { name: string; age: string }

Read it as: “For each K in keyof T, set the property type to string.”

Make All Optional

type Optional<T> = {
  [K in keyof T]?: T[K];
};

type PartialUser = Optional<User>;
// { name?: string; age?: number }

The ?: adds the optional modifier. This is exactly what the built-in Partial<T> does.

Make All Readonly

type ReadOnly<T> = {
  readonly [K in keyof T]: T[K];
};

The built-in Readonly<T>.

Remove Modifiers — -? and -readonly

type Required<T> = {
  [K in keyof T]-?: T[K];     // strip optional
};

type Mutable<T> = {
  -readonly [K in keyof T]: T[K];   // strip readonly
};

-? strips ?:. -readonly strips readonly. Often used inside larger transformations.

Transform the Value Type

type Promisify<T> = {
  [K in keyof T]: T[K] extends (...args: infer A) => infer R
    ? (...args: A) => Promise<R>
    : T[K];
};

Wraps every method’s return in a Promise. Uses conditional and infer (covered shortly).

Key Remapping with as

You can rename keys as you map:

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

type User = { name: string; age: number };
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number }

The Capitalize<...> is a built-in string transformer; we cover template literal types next lesson.

Filtering Keys With never

To exclude keys, map them to never and then filter:

type StringKeys<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

type Mixed = { name: string; age: number; tags: string };
type Strings = StringKeys<Mixed>;
// { name: string; tags: string }

Mapped types + conditionals + as rename = serious power.

Up Next

Template literal types — for string-level type manipulation.

Template Literal Types →