stackademic

The leading education platform for anyone with an interest in software development.

TypeScript Mapped Types

Transform every property of a type with key remapping and modifiers

Overview

Mapped types iterate over the keys of an existing type to produce a new one, applying a transformation to each property. They use the [K in keyof T] syntax and support modifiers like readonly and ?, plus key remapping via as. Utility types such as Partial, Readonly, and Record are all mapped types.

Syntax / Usage

Iterate over keyof T and optionally add or remove modifiers. A leading + or - explicitly adds or strips readonly/optional.

// Make every property optional
type MyPartial<T> = {
  [K in keyof T]?: T[K]
}

// Make every property required and mutable
type Concrete<T> = {
  -readonly [K in keyof T]-?: T[K]
}

interface Config {
  readonly host?: string
  readonly port?: number
}

type FullConfig = Concrete<Config>
// { host: string; port: number }

Examples

Remap keys with as to build a set of event handler names.

type Handlers<T> = {
  [K in keyof T as `on${Capitalize<string & K>}`]: (value: T[K]) => void
}

interface State {
  count: number
  name: string
}

type StateHandlers = Handlers<State>
// { onCount: (value: number) => void; onName: (value: string) => void }

Filter out properties by remapping unwanted keys to never.

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

type S = OnlyStrings<{ id: number; name: string; slug: string }>
// { name: string; slug: string }

Build a lookup type from a union of keys.

type Flags<K extends string> = {
  [P in K]: boolean
}

type FeatureFlags = Flags<"beta" | "darkMode"> // { beta: boolean; darkMode: boolean }

Common Mistakes

  • Forgetting keyof, iterating over a value instead of its keys
  • Omitting the - prefix when you intend to strip readonly or optional
  • Remapping to never accidentally, silently dropping properties
  • Using a mapped type where a simple Record would be clearer
  • Expecting method implementations to survive—only the type shape is mapped

See Also

typescript-conditional-types utility-types generics