---
title: "TypeScript: Utility Types"
slug: "typescript-utility-types"
category: "coding"
tags: ["coding", "typescript", "utility-types", "mapped-types", "inference"]
status: "stable"
last_updated: 2026-05-14
summary: "Pick, Omit, Partial, Required, Readonly, Record, Exclude, Extract, NonNullable, ReturnType, Awaited; and when to roll your own."
related: ["[[coding/typescript]]", "[[coding/typescript-generics]]", "[[coding/typescript-types-vs-interfaces]]", "[[coding/typescript-narrowing]]", "[[coding/typescript-strict-mode]]"]
---

## Overview

Utility types are the built-in helpers that derive one type from another. Use them instead of rewriting the same shape by hand; when they fall short, write a small mapped or conditional type. Read [[coding/typescript-generics]] first; every utility below is a generic over an existing type.

## `Pick` and `Omit` to subset object types

`Pick<T, K>` keeps the named keys; `Omit<T, K>` drops them. Use these when a function takes a subset of an existing shape.

```ts
type User = {
  id: string
  email: string
  passwordHash: string
  createdAt: Date
}

type PublicUser = Omit<User, "passwordHash">
type LoginInput = Pick<User, "email"> & { password: string }
```

When a function returns "the input minus one field," `Omit` keeps the two types in lockstep. Rename the field and both update. See [[coding/typescript-types-vs-interfaces]] for when `&` is the wrong combiner.

## `Partial` for patches; `Required` to lock fields in

`Partial<T>` makes every field optional. Use it for PATCH-style update payloads.

```ts
type User = { id: string; name: string; bio: string }

async function updateUser(id: string, patch: Partial<Omit<User, "id">>) {
  // ...
}
```

`Required<T>` does the inverse: makes every field non-optional. Use it after validation to assert that a previously-loose input is now fully populated.

```ts
type Draft = { name?: string; price?: number }
type Final = Required<Draft>  // { name: string; price: number }
```

`Partial` is the most overused utility; do not reach for it as a shortcut around modeling. A type with every field optional is a type that says nothing.

## `Readonly` for immutable shapes

`Readonly<T>` marks every field `readonly`. Use it for config, constants, and any value passed by reference that should not be mutated.

```ts
const CONFIG: Readonly<{ apiBase: string; retries: number }> = {
  apiBase: "https://api.example.com",
  retries: 3,
}

// CONFIG.retries = 5  // Error
```

`readonly` is shallow. For deep immutability, write a `DeepReadonly<T>` helper or, more often, freeze the value with `Object.freeze` and trust the type system at the top level.

## `Record` for typed key-value maps

`Record<K, V>` types an object whose keys are `K` and values are `V`. Use it for lookup tables and switch-replacement maps.

```ts
type Status = "draft" | "sent" | "paid"

const LABELS: Record<Status, string> = {
  draft: "Draft",
  sent: "Sent",
  paid: "Paid",
}
```

The compiler errors if you add a new status to the union and forget to add a label. That is the win; without `Record`, the map drifts from the union silently. Pair with `as const` (see [[coding/typescript]]) for literal preservation.

## `Exclude`, `Extract`, and `NonNullable` for unions

These three work on union types, not object types. Confusing them with `Pick`/`Omit` is the most common utility-type mistake.

```ts
type Status = "draft" | "sent" | "paid" | "void"

type Live = Exclude<Status, "void">           // "draft" | "sent" | "paid"
type Closed = Extract<Status, "paid" | "void">// "paid" | "void"

type MaybeUser = User | null | undefined
type DefiniteUser = NonNullable<MaybeUser>    // User
```

`NonNullable<T>` is `Exclude<T, null | undefined>`. Reach for it after a check, when you want to express "I have already proven this is set." See [[coding/typescript-narrowing]] for the runtime side of the same idea.

## `ReturnType` and `Parameters` for function inference

`ReturnType<F>` is the return type of `F`; `Parameters<F>` is its tuple of arguments. Use these when a function's signature is the source of truth and you do not want to restate it.

```ts
function loadUser(id: string) {
  return { id, name: "Ada" } as const
}

type LoadedUser = ReturnType<typeof loadUser>      // { id: string; name: "Ada" }
type LoadArgs = Parameters<typeof loadUser>        // [id: string]
```

This pattern is the right tool for "the return type matters but spelling it out duplicates the function body." It is the wrong tool for designing an API; design the type first, then write the function to fit.

## `Awaited` to unwrap promises

`Awaited<T>` resolves nested promises to the eventual value. The compiler uses it internally for `await`; use it explicitly when the source is a `Promise<Promise<T>>` or a `ReturnType` of an async function.

```ts
async function loadInvoice(id: string) {
  return { id, amount: 100 }
}

type Invoice = Awaited<ReturnType<typeof loadInvoice>>  // { id: string; amount: number }
```

Before `Awaited`, the idiom was a hand-rolled conditional with `infer`. The built-in handles the recursive unwrap correctly; use it.

## Roll your own when the built-ins fall short

The built-ins cover 80% of cases. For the rest, a small mapped or conditional type is the answer. Three patterns worth knowing:

```ts
// Strict pick: error if a key in K is not in T (Pick allows extras silently).
type StrictPick<T, K extends keyof T> = { [P in K]: T[P] }

// Mutable: drop readonly from every field.
type Mutable<T> = { -readonly [K in keyof T]: T[K] }

// PartialBy: make a specific subset of keys optional, keep the rest required.
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
```

Name the helper, place it in a `types.ts` module, and reuse it. Avoid pasting the same five-line type expression into ten files; the named alias survives refactors better. See [[coding/typescript-generics]] for the conditional-type and `infer` rules.

## Related

- [[coding/typescript]]
- [[coding/typescript-generics]]
- [[coding/typescript-types-vs-interfaces]]
- [[coding/typescript-narrowing]]
- [[coding/typescript-strict-mode]]
