Advanced Patterns

Mapped Types

Mapped types iterate over the keys of a type to create a new type by transforming each property, enabling systematic type transformations.

Definition

TypeScript
type Mapped<T> = {
  [K in keyof T]: NewType<T[K]>;
};

Examples

Making All Properties Nullable
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

interface User {
  name: string;
  age: number;
  active: boolean;
}

type NullableUser = Nullable<User>;
// { name: string | null; age: number | null; active: boolean | null }
Key Remapping with as
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

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

type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number }
Filtering Keys
type OnlyStrings<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

interface Mixed {
  name: string;
  age: number;
  email: string;
  active: boolean;
}

type StringProps = OnlyStrings<Mixed>;
// { name: string; email: string }

Common Use Cases

  • 1Creating readonly, optional, or nullable variants of types
  • 2Generating getter/setter interfaces from data types
  • 3Filtering object keys by value type
  • 4Renaming keys with template literal types
  • 5Building type-safe ORM query builders

Understanding Mapped Types

Mapped types are a foundational feature of TypeScript's type system that allow you to create new types by transforming each property of an existing type. The syntax [K in keyof T] iterates over every key of T, and you can modify the key, the value type, or add/remove modifiers for each property.

The built-in utility types Partial, Required, Readonly, and Record are all implemented as mapped types. Understanding mapped types means understanding the building blocks that these utility types are made of, and the ability to create your own custom transformations.

Mapped types support modifier manipulation with + and -. Adding +readonly makes properties readonly, while -? removes the optional modifier (making properties required). This modifier syntax is what powers the Required utility type.

TypeScript 4.1 introduced key remapping with the as clause, dramatically expanding what mapped types can express. With as, you can rename keys using template literal types, filter keys by removing them (mapping to never), or create entirely new key sets from existing types. This is the mechanism behind patterns like generating getter/setter methods from property names.

Filtering with as is particularly powerful. By writing [K in keyof T as T[K] extends string ? K : never], you create a new type containing only the string-valued properties of T. The never type in key position removes the property entirely.

Mapped types can also iterate over arbitrary unions, not just keyof T. You can write [K in "a" | "b" | "c"] to create an object with exactly those three keys. Combined with template literal types and conditional types, mapped types form a Turing-complete meta-programming system within TypeScript's type language.

Related Types

More Advanced Patterns

Explore TypeScript Types

Browse our complete reference of 30 TypeScript utility types with definitions, examples, and explanations.