Skip to content

Mapped Types

A mapped type iterates over the keys of another type and produces a new type.

The basic syntax:

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

This means:

  • “For each key K in T, produce a property with the same key and value type.”

Let’s explore how to use this pattern to transform types.


Transforming Object Types

Mapped types allow you to modify:

  • property types
  • property modifiers (readonly, ?)
  • keys
  • value shapes

This is how TypeScript’s built‑in utility types work.


Example: Make all properties optional

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

Usage:

type User = { id: string; name: string }

type UserUpdate = MyPartial<User>
// { id?: string; name?: string }

This is exactly how Partial<T> is implemented.


Example: Make all properties readonly

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

Usage:

type Config = { port: number; debug: boolean }

type FrozenConfig = MyReadonly<Config>
// { readonly port: number; readonly debug: boolean }

Example: Remove readonly

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

The -readonly modifier removes the readonly flag.


Example: Remove optional

type RequiredProps<T> = {
  [K in keyof T]-?: T[K]
}

The -? modifier removes optionality.


Transforming Value Types

Mapped types can also transform the value types.

Example: Convert all properties to booleans

type Flags<T> = {
  [K in keyof T]: boolean
}

Usage:

type Permissions = { read: string; write: string }

type PermissionFlags = Flags<Permissions>
// { read: boolean; write: boolean }

Example: Wrap all properties in a Promise

type Asyncify<T> = {
  [K in keyof T]: Promise<T[K]>
}

Usage:

type User = { id: number; name: string }

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

This is extremely useful for API layers and async transformations.


Key Remapping (as)

TypeScript 4.1 introduced key remapping, which allows you to change the keys of a mapped type.

This unlocks powerful transformations.


Example: Prefix all keys

type PrefixKeys<T, P extends string> = {
  [K in keyof T as `${P}${string & K}`]: T[K]
}

Usage:

type User = { id: string; name: string }

type ApiUser = PrefixKeys<User, "api_">
// { api_id: string; api_name: string }

Example: Filter keys

You can remove keys by mapping them to never.

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

Usage:

type Mixed = {
  id: string
  log(): void
}

type NoFunctions = RemoveFunctions<Mixed>
// { id: string }

This is how you build “pick all non‑function properties” utilities.


Example: Convert keys to uppercase

type UppercaseKeys<T> = {
  [K in keyof T as Uppercase<string & K>]: T[K]
}

Usage:

type User = { id: string; name: string }

type UpperUser = UppercaseKeys<User>
// { ID: string; NAME: string }

Combining Key Remapping + Value Transformation

Mapped types can do both at once.

Example: Create getter methods for each property

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

Usage:

type User = { id: number; name: string }

type UserGetters = Getters<User>
// {
//   getId: () => number
//   getName: () => string
// }

This is how ORMs and state libraries generate dynamic APIs.


Putting It All Together

Here’s a realistic example combining everything:

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

Usage:

type User = { id: string; age: number }

type UserModel = Model<User>

Result:

{
  getId(): string
  getAge(): number
  setId(value: string): void
  setAge(value: number): void
}

This is the foundation of:

  • ORMs
  • form builders
  • state management libraries
  • code generation tools

Mapped types are the engine behind many advanced TypeScript patterns.


Summary

In this lesson, you learned how mapped types let you transform object types:

1. Transforming object types

  • add/remove readonly
  • add/remove optionality
  • change value types
  • wrap values in other types

2. Key remapping

  • rename keys
  • prefix/suffix keys
  • filter keys using never
  • generate new APIs dynamically

Mapped types are one of the most expressive features in TypeScript—they let you reshape types with the same flexibility that JavaScript gives you for reshaping objects.