Skip to content
webtype.orgwebtype.org

    ↑↓ 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 6.0.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.