typescript · level 5

Utility Types

Partial, Pick, Omit, Record, ReturnType — the tools you should reach for first.

175 XP

Utility Types

TypeScript ships about a dozen built-in utility types — small, generic transformations over object types. Knowing them by name saves you from reinventing every transformation. Here are the ones you'll use weekly, in order of frequency.

The big four

Partial<T>

Makes every property optional.

interface User { id: number; name: string; email: string }
type UserDraft = Partial<User>;
// → { id?: number; name?: string; email?: string }

Use cases: PATCH endpoint payloads, form drafts, in-progress object construction. Anywhere "the same shape, but maybe missing fields".

Pick<T, K>

Selects a subset of properties.

type PublicUser = Pick<User, "id" | "name">;
// → { id: number; name: string }

Use cases: API response shapes that hide internal fields, function arguments that need only a slice of an entity.

Omit<T, K>

The dual of Pick — removes a subset.

type SafeUser = Omit<User, "email">;
// → { id: number; name: string }

When the exclusion list is shorter than the inclusion list, Omit reads more naturally:

// Same result, but Omit is clearly easier to read.
Pick<User, "id" | "name">       // 2 keys to include
Omit<User, "email">             // 1 key to exclude

Record<K, V>

Builds an object whose keys are K and values are V.

type Color = "red" | "green" | "blue";
type Palette = Record<Color, string>;
// → { red: string; green: string; blue: string }

type Counts = Record<string, number>;
// → { [k: string]: number }   ← string-keyed dictionary

Use cases: enumerated maps (every member of a union has a value), string-keyed dictionaries, lookup tables.

The function-type reflectors

These pull types out of existing function and promise types:

ReturnType<F>

function loadUser(id: number) { return { id, name: "x" }; }
type Loaded = ReturnType<typeof loadUser>;
// → { id: number; name: string }

The typeof loadUser is the function's type (not a value). ReturnType reads the return slot of that function type. Massively useful when the function's return type is anonymous (object literal).

Parameters<F>

type LoadParams = Parameters<typeof loadUser>;
// → [id: number]

Returns a tuple of the parameter types — order preserved.

Awaited<T>

Unwraps Promise<X> recursively (handles Promise<Promise<X>> too):

type R = Awaited<Promise<{ ok: boolean }>>;
// → { ok: boolean }

Often combined with ReturnType for async functions:

async function fetchUser() { /* ... */ return { id: 1 } }
type FetchedUser = Awaited<ReturnType<typeof fetchUser>>;
// → { id: number }

The modifier toggles

Required<T>

Inverse of Partial — makes every property required:

type Filled = Required<UserDraft>;  // { id: number; name: string; email: string }

Readonly<T>

Marks every property readonly:

type Frozen = Readonly<User>;
// frozen.id = 5  ❌ Cannot assign to 'id' because it is a read-only property

NonNullable<T>

Strips null and undefined:

type S = NonNullable<string | null | undefined>;  // string

The class reflectors

Less commonly used, but real:

class Service { constructor(public name: string, public port: number) {} }

type SvcInstance = InstanceType<typeof Service>;        // Service
type SvcCtorArgs = ConstructorParameters<typeof Service>; // [name: string, port: number]

If you've ever written a generic factory that takes a class and produces an instance, you've used these.

Composition is fine

Utility types are just generic types — compose them freely:

type ApiResponse = Readonly<Required<Pick<User, "id" | "name">>>;
// → readonly id: number; readonly name: string

type MaybeUser = Partial<NonNullable<User>>;

There's no "depth limit" or perf cliff for sensible nesting. If a composition is hard to read, alias the inner pieces:

type PublicUser = Pick<User, "id" | "name">;
type ApiUser    = Readonly<PublicUser>;

What they look like underneath

Each utility is one of three kinds of type:

  • Mapped types (Partial, Required, Readonly, Record, Pick):
    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 Record<K extends PropertyKey, V> = { [P in K]: V };
    type Pick<T, K extends keyof T> = { [P in K]: T[P] };
    
  • Conditional types with infer (ReturnType, Parameters, Awaited, InstanceType):
    type ReturnType<F> = F extends (...args: any) => infer R ? R : never;
    type Parameters<F> = F extends (...args: infer P) => any ? P : never;
    
  • Union exclusion (Exclude, Extract, Omit, NonNullable):
    type Exclude<T, U>     = T extends U ? never : T;
    type Extract<T, U>     = T extends U ? T : never;
    type Omit<T, K>        = Pick<T, Exclude<keyof T, K>>;
    type NonNullable<T>    = T & {};   // (effectively)
    

You don't need to memorise these definitions, but knowing they're just regular generics demystifies them — when you need a custom one, you write one with the same machinery.

When to write your own

The built-ins solve common cases. When they don't fit — say you need "Pick where each value becomes optional" — you compose:

type PartialPick<T, K extends keyof T> = Partial<Pick<T, K>>;

Or you write one from scratch:

// Only the keys whose values are functions
type FunctionKeys<T> = {
  [K in keyof T]: T[K] extends (...args: any) => any ? K : never;
}[keyof T];

The bigger your utility-type library, the more your application code looks like data wrangling — exactly the level of abstraction TypeScript is best at.

Tools in the wild

3 tools
  • type-festfree tier

    120+ utility types — RequireAtLeastOne, Merge, Promisable, SetOptional, etc.

    library
  • ts-essentialsfree tier

    DeepPartial, DeepReadonly, DeepRequired and other recursive variants.

    library
  • Use `// $ExpectType` and hover to verify the resolved structure of compositions.

    service