Skip to content

The Utility Types

TypeScript ships a standard library of utility types. By now you have seen every technique they use — so instead of memorizing them, you can read them. That is the goal of this lesson: recognize each utility and know the primitive behind it.

UtilityWhat it doesHow it is built
Partial<T>All properties optionalMapped type with ?
Required<T>All properties requiredMapped type with -?
Readonly<T>All properties readonlyMapped type with readonly
Pick<T, K>Keep only keys KMapped type over K + T[K]
Omit<T, K>Drop keys KPick of Exclude<keyof T, K>
Record<K, V>Object with keys K, values VMapped type over K
Exclude<T, U>Remove U from union TDistributive conditional
Extract<T, U>Keep only U from union TDistributive conditional
NonNullable<T>Remove null/undefinedConditional type
ReturnType<F>The return type of FConditional with infer
Parameters<F>The parameter tuple of FConditional with infer
Awaited<T>Unwrap a PromiseRecursive conditional with infer

Partial, Required, Readonly, Pick, and Record are all one-line mapped types:

type Partial<T> = { [K in keyof T]?: T[K] };
type Required<T> = { [K in keyof T]-?: T[K] };
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
type Record<K extends keyof any, V> = { [P in K]: V };

Nothing exotic — a loop over keys, sometimes changing a modifier, sometimes looking up T[K]. Once you see this, Pick<User, "id" | "name"> is obviously “iterate those two keys, copy their value types.”

Exclude, Extract, and NonNullable operate on unions using a conditional type that distributes over each member:

type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
type NonNullable<T> = T extends null | undefined ? never : T;
type A = Exclude<"a" | "b" | "c", "b">; // "a" | "c"

When the checked type is a naked type parameter and you pass a union, the conditional runs once per member and unions the results. So Exclude maps each member to either never (drop it) or itself (keep it) — and never disappears from a union. Omit then falls out as Pick<T, Exclude<keyof T, K>>.

ReturnType, Parameters, and Awaited capture a type out of a structure with infer:

type ReturnType<F> = F extends (...args: any[]) => infer R ? R : never;
type Parameters<F> = F extends (...args: infer P) => any ? P : never;
type R = ReturnType<() => number>; // number
type P = Parameters<(a: string, b: number) => void>; // [string, number]

infer R matches the function return position and binds it to R; infer P matches the whole parameter list as a tuple. Awaited adds recursion to peel nested promises.

flowchart TB
  mapped["Mapped types"] --> f1["Partial, Required, Readonly, Pick, Record"]
  cond["Distributive conditionals"] --> f2["Exclude, Extract, NonNullable, Omit"]
  infer["Conditionals with infer"] --> f3["ReturnType, Parameters, Awaited"]
Each utility maps to one primitive you already know
How is `Partial<T>` implemented?
What technique powers `Exclude<T, U>`?
How does `ReturnType<F>` extract the return type?
How is `Omit<T, K>` typically defined?