Capítulo 10 de 36

Chapter 10: Mapped Types

Core Idea

A mapped type iterates every key of an existing type (usually via keyof) to produce a new type — the mechanism behind almost every built-in utility type (Partial, Required, Readonly, Pick, Record).

Key Concepts

  • Basic form: type OptionsFlags<Type> = { [Property in keyof Type]: boolean } — walks every key of Type and gives each one a new value type (here, boolean for all of them regardless of the original type).
  • Mapping modifiers (readonly and ?): can be added or stripped during mapping by prefixing with + (default, add) or - (remove). { -readonly [P in keyof T]: T[P] } strips readonly from every property; { [P in keyof T]-?: T[P] } strips optionality, making every property required.
  • Key remapping via as (TS 4.1+): { [P in keyof T as NewKey]: T[P] } lets the mapped type rename keys as it iterates, not just retype values. Commonly combined with template literal types to derive new names (get${Capitalize<...>}), producing a "getters" version of an interface.
  • Filtering keys out: mapping a key as never in the remap clause removes it from the resulting type — the standard way to drop a specific property (e.g. via Exclude<Property, "kind"> in the as clause) while mapping the rest through unchanged.
  • Mapping over non-string unions: the iterated union doesn't have to be keyof output — it can be a union of any type with a suitable key expression, e.g. mapping over a union of event-object types keyed by their own kind discriminant field to build an event-name → handler config type.
  • Composes with conditional types: a mapped type's per-property value can itself be a conditional type, e.g. producing a true/false flag type per property based on whether that property's value matches some shape.

Code Examples

type LockedAccount = { readonly id: string; readonly name: string };
type CreateMutable<T> = { -readonly [P in keyof T]: T[P] };
type UnlockedAccount = CreateMutable<LockedAccount>; // { id: string; name: string }
  • What it demonstrates: stripping readonly from every property of a type via a mapping modifier.
type Getters<T> = {
  [P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
interface Person { name: string; age: number }
type LazyPerson = Getters<Person>; // { getName: () => string; getAge: () => number }
  • What it demonstrates: key remapping with as plus a template literal type to derive new property names from the original ones.

Reference Tables

ModifierMeaning
[P in keyof T]: Xbase mapped type
-readonly [P in keyof T]remove readonly from every property
+readonly / bare readonlyadd readonly (default when prefix omitted)
[P in keyof T]-?:remove optionality (make required)
[P in keyof T as NewName]:remap the key name during iteration
as never in the as clausedrop that key from the result entirely

Key Takeaways

  1. Reach for a mapped type any time you need "the same shape as T, but every property is X" — it's the general mechanism behind Partial<T>/Required<T>/Readonly<T>.
  2. Use key remapping (as) to both rename and filter properties in one pass, instead of a separate Omit/Pick step.
  3. Mapped types combine naturally with conditional types (per-property logic) and template literal types (per-property renaming) — most advanced utility types are some composition of the two.

Connects To

  • Keyof/Typeof Operators: keyof T is the standard source for the iterated union.
  • Conditional Types: used inside mapped types for per-property logic.
  • Template Literal Types: used inside as clauses to derive new key names.
  • Utility Types (Reference): Partial, Required, Readonly, Pick, Record are all mapped types under the hood.