Skip to content
webtype.orgMapped types

    ↑↓ move · ⏎ open · esc close

    Constructs

    Mapped types

    A loop over a union of keys that builds an object type.

    What it is

    Read `{ [K in Keys]: Value }` as: for every `K` in this key set, produce a property named `K` whose type is that expression. Two independent decisions per key — what it is called, via the optional `as` clause, and what it holds. `Partial`, `Required`, `Readonly`, `Pick` and `Record` are each one line of this.

    Examples

    • Stringify<User>
      { id: string; name: string; }
    • Prefixed<User>
      { getId: number; getName: string; }

      The `as` clause renames the key. This is how a mapped type produces names that were not in the input.

    • Filtered<User>
      { name: string; }

      Renaming a key to `never` removes it. That is the only way a mapped type can drop a property.

    Each resolved type above was printed by TypeScript 5.9.3, not written by hand.

    What it does not do

    • It cannot remove a key from the value side. Producing `never` as a property *type* leaves the key present and unfillable, which is not the same as absent.
    • It stops preserving modifiers the moment the key set is computed. `[K in keyof T]` is homomorphic and keeps `readonly` and `?`; add an `as` clause or union the keys and they are dropped unless you re-add them.

    Takeaway

    Keys are decided in the key clause and values in the value clause. Most mapped-type bugs are one of those two answers accidentally used for the other.