TypeScript

TypeScript Mapped Types👨‍💻

Mapped types iterate over the keys of an existing type and produce a new type with transformed properties. They are the mechanism behind Partial, Required, Readonly, and Pick -- and once you understand how they work, you can build your own utility types that fit your codebase exactly. If you have ever written a type by hand that mirrors another type with slight modifications, a mapped type would have done it in three lines.

Key Takeaways

  • 1The syntax `{ [K in keyof T]: ... }` iterates over every key of `T` and creates a new property for each -- think of it as a `for...in` loop at the type level
  • 2Modifier operators `+?` / `-?` and `+readonly` / `-readonly` add or remove optionality and immutability from properties during the mapping
  • 3Key remapping with `as` (TypeScript 4.1+) lets you rename, prefix, or filter keys mid-iteration -- returning `never` from the `as` clause removes the key entirely
  • 4You are not limited to `keyof T` -- you can map over any union of string literals, which is how `Record<K, V>` works under the hood
  • 5Combining mapped types with conditional types in the value position lets you selectively transform properties based on their type (e.g., converting all `Date` fields to `string` for serialization)
  • 6Most of the built-in utility types (`Partial`, `Required`, `Readonly`, `Pick`, `Record`) are one-liner mapped types -- reading their source teaches you the pattern

Master typescript mapped types

Take the TypeScript Type System Mastery course with hands-on lessons and challenges.

Examples

Patch DTO -- make all fields nullable for API updates

typescript

Partial alone makes properties optional, but does not allow null. In real APIs, PATCH operations often need to distinguish between "field not sent" (undefined) and "field explicitly cleared" (null). This mapped type handles both.

Read-only database model from a mutable insert type

typescript

Readonly is itself a mapped type that adds the readonly modifier to every property. The -readonly modifier strips it back off. This pattern keeps your insert types lean and derives the read types automatically.

Form validators derived from a schema type

typescript

The mapped type guarantees exhaustive coverage. If someone adds a new field to RegistrationForm, TypeScript immediately flags the missing validator. The parameter type is inferred from the field, so you cannot accidentally validate a string as a number.

Key remapping -- filter to function properties only

typescript

The as clause with a conditional that returns never filters out non-function keys entirely. This is cleaner than Pick with a complex key extraction type, and it stays in sync automatically when the source interface changes.

Converting a sync API to async with key remapping

typescript

This combines three features in one mapped type: key remapping with template literals to rename methods, conditional types with infer to extract parameter and return types, and Promise wrapping to convert synchronous signatures to async. You see this pattern when adapting local APIs into remote service interfaces.

Record-style mapped type over a custom union

typescript

Record<K, V> is just syntactic sugar for { [P in K]: V }. Writing the mapped type yourself becomes useful when you want different value types per key, or when you combine it with conditional types to vary the shape based on the key.

Common Mistakes

Mistake:

Writing `[K in T]` instead of `[K in keyof T]` -- forgetting that you need `keyof` to extract the keys from an object type

Fix:

Always use `keyof` to get the property names: `[K in keyof T]: T[K]`. Without `keyof`, TypeScript expects `T` itself to be a union of strings/numbers/symbols, which is only valid when you are intentionally mapping over a custom union like `[K in "a" | "b"]`.

Mistake:

Using `Capitalize<K>` directly in a key remapping when `K` could be a symbol -- this causes a type error because Capitalize only accepts strings

Fix:

Intersect with `string` first: `Capitalize<string & K>`. Since `keyof T` can include symbols and numbers, the intersection narrows K to only string keys. This is required in every mapped type that uses template literal transformations on keys.

Mistake:

Expecting that adding `| undefined` to a property value is the same as making it optional with `?` -- e.g., writing `{ [K in keyof T]: T[K] | undefined }` when you wanted `Partial`

Fix:

Adding `| undefined` makes the value nullable but the key is still required. Use `[K in keyof T]?: T[K]` with the `?` modifier to actually make properties optional. With `exactOptionalPropertyTypes` enabled, the difference is even more visible.

Mistake:

Building overly complex single mapped types that combine filtering, remapping, and conditional value transformation -- making the type unreadable and hard to debug

Fix:

Compose smaller utility types instead. Write a `MethodsOf<T>` that filters, then a `Prefixed<T>` that renames, then combine them: `Prefixed<MethodsOf<Service>>`. Each type is testable and understandable on its own.

Best Practices

  • Prefer built-in utility types (`Partial`, `Required`, `Readonly`, `Pick`, `Record`) when they fit your use case exactly -- write custom mapped types only when you need behavior they do not cover
  • Compose small mapped types instead of writing one large one: `Readonly<Partial<T>>` is immediately clear, while a single mapped type with `-readonly` and `+?` combined takes a second look to parse
  • Use the `as never` pattern for key filtering instead of chaining `Pick` and `Omit` with complex key extraction types -- it keeps the logic in one place and is easier to follow
  • Add a `Simplify<T>` helper (`type Simplify<T> = { [K in keyof T]: T[K] }`) to flatten intersections when hovering over types in your IDE shows an unreadable mess
  • Document custom mapped types with a one-line comment showing an example input/output -- mapped types are powerful but their results are not always obvious to the next person reading your code

Summary

Mapped types transform existing types by iterating over their keys with `[K in keyof T]` syntax. Modifier operators (`+?`, `-?`, `+readonly`, `-readonly`) control optionality and immutability. Key remapping with `as` (TS 4.1+) enables renaming keys via template literals and filtering keys by returning `never`. In practice, you will use mapped types to build patch DTOs, derive form validators from schemas, extract method-only interfaces, and convert between sync and async API signatures. The built-in utility types are all mapped types -- understanding the pattern lets you build exactly what your codebase needs.

Practice TypeScript with hands-on challenges

Learn typescript mapped types hands-on in your IDE

Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.

Related Concepts

Related Cheatsheets

Master TypeScript with Stanza

Interactive lessons and challenges, right in your code editor.

Check the free courses. No credit card.