thecodex.expert · The Codex Family of Knowledge
TypeScript

Mapped Types

Partial<T>, Readonly<T>, and Pick<T> all look like built-in magic — they’re actually ordinary mapped types, defined in TypeScript’s own standard library, that you can write yourself.

TypeScript 6.0 Partial, Readonly, Pick Last verified:
Canonical Definition

A mapped type has the form { [K in Keys]: Transform }, iterating over a set of keys (often keyof SomeType) and applying a transformation to each property. This is exactly how TypeScript's own built-in utility types — Partial<T>, Readonly<T>, Pick<T, K> — are implemented in the standard library, not special compiler magic; anyone can write an equivalent mapped type from scratch.

A basic mapped type

keyof extracts a union of a type's property names, and the mapped type iterates over that union, producing a new type with the transformation applied to every one.

TypeScriptbasic_mapped.ts
interface User {
    name: string;
    age: number;
}

type ReadonlyUser = {
    [K in keyof User]: User[K];   // same shape, but see the readonly version below
};

type MyReadonly<T> = {
    readonly [K in keyof T]: T[K];   // this is essentially how Readonly<T> is built
};

type FrozenUser = MyReadonly<User>;   // { readonly name: string; readonly age: number }

Modifiers: adding or removing readonly and optional

+/- prefixes on readonly or ? let a mapped type explicitly add or strip those modifiers, rather than just preserving whatever the source type already had.

TypeScriptmodifiers.ts
type MyPartial<T> = {
    [K in keyof T]?: T[K];   // essentially how Partial<T> is built
};

type MyRequired<T> = {
    [K in keyof T]-?: T[K];   // the -? REMOVES optionality (essentially Required<T>)
};

Key remapping with as

The as clause (introduced in TypeScript 4.1) lets a mapped type rename keys during the transformation, not just transform their values — useful for generating a companion type with prefixed or derived property names.

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

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

Sources

1
Microsoft. TypeScript Handbook, "Mapped Types", typescriptlang.org/docs/handbook/2/mapped-types.html.