Skip to content
TypeScript ts advanced-types 4 min read

Mapped Types

Mapped types let you build a new object type by transforming each property of an existing one. The syntax { [K in keyof T]: ... } iterates over a union of keys and produces a property for each — the type-level equivalent of Object.keys(obj).map(...). With modifiers you can add or remove readonly and optional (?), and with key remapping (as) you can rename or filter keys entirely. Every built-in utility type like Partial, Readonly, and Pick is a mapped type. This page covers the core syntax, modifiers, remapping, and value transformation.

Basic mapped type syntax

A mapped type walks each key K in a union (usually keyof T) and defines what that property’s type should become. The result is a brand-new object type.

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

interface User {
  id: number;
  active: boolean;
}

type StringUser = Stringify<User>;
// { id: string; active: string }

[K in keyof T] is the iteration. For each key the value type is set to string, so every property of User becomes a string. You can reference the original value type with T[K] to transform rather than replace it.

Modifiers: readonly and optional

Mapped types can add readonly and ? to every property, or strip them off with the - prefix. Use +readonly/+? to add (the + is optional and usually omitted) and -readonly/-? to remove.

// Add modifiers — this is how Readonly and Partial are defined:
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Partial<T> = { [K in keyof T]?: T[K] };

// Remove modifiers:
type Mutable<T> = { -readonly [K in keyof T]: T[K] };
type Required<T> = { [K in keyof T]-?: T[K] };

interface Config {
  readonly host?: string;
}

type Solid = Required<Mutable<Config>>;
// { host: string }  — readonly and ? both removed

The -readonly modifier strips readonly, and -? strips optionality (which also removes undefined from the value). Applying both to Config turns readonly host?: string into a plain required host: string.

ModifierEffect
readonly [K in ...]makes every property readonly
-readonly [K in ...]removes readonly
[K in ...]?makes every property optional
[K in ...]-?removes optional (and undefined)

Key remapping with as

Since TypeScript 4.1 you can remap keys with an as clause. This lets you rename keys — often with template literal types — or filter keys out by mapping them to never.

// Rename keys: create getters like getId, getName.
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 }

The as clause computes a new key for each property. Mapping a key to never removes it entirely, which is how you filter:

// Keep only the string-valued properties.
type StringKeys<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

type OnlyStrings = StringKeys<{ a: string; b: number; c: string }>;
// { a: string; c: string }

Filtering by remapping a key to never is the standard way to omit properties conditionally — there is no other built-in syntax for dropping keys inside a mapped type.

Transforming value types

Mapped types shine when you combine iteration with a transformation of T[K], optionally using a conditional type per property.

// Wrap every property value in a Promise.
type Promisify<T> = {
  [K in keyof T]: Promise<T[K]>;
};

type AsyncUser = Promisify<{ id: number; name: string }>;
// { id: Promise<number>; name: Promise<string> }

// Make function-valued props stay, turn everything else nullable.
type Nullable<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : T[K] | null;
};

Promisify rewraps each value, and Nullable uses a conditional to treat methods differently from data. Because the transformation runs per key, each property can end up with a different result type.

Best Practices

  • Iterate with [K in keyof T] and reference T[K] to transform rather than discard value types.
  • Use ? and readonly modifiers (with +/-) to add or strip optionality and immutability.
  • Remap keys with as, and template literals, to rename properties consistently.
  • Filter properties by remapping unwanted keys to never.
  • Prefer the built-in Partial, Required, Readonly, Pick, and Record before hand-rolling equivalents.
  • Combine mapped types with conditional types to vary the transform per property.
Last updated June 29, 2026
Was this helpful?