Utility types are built-in type transformations that ship with TypeScript. They handle the patterns you'd otherwise write by hand — making properties optional for PATCH endpoints, stripping sensitive fields from API responses, extracting return types from functions you don't control. They're not magic: every one of them is implemented with mapped types and conditional types under the hood, which means you can read their source and build your own when the built-ins don't fit.
Master typescript utility types
Take the TypeScript Essentials course with hands-on lessons and challenges.
Partial<Omit<Product, "id">> is the go-to pattern for PATCH operations. Omit removes the immutable id first, then Partial makes everything else optional. The caller sends only what changed, and TypeScript still validates that the field names and types are correct.
Define one source-of-truth type (usually matching your database row), then derive every API shape from it. Omit strips sensitive fields; Pick selects just what a specific view needs. When the base type changes, all derived types update automatically.
When the key type is a finite union, Record forces you to handle every member. Add a new status code to HttpStatus and TypeScript immediately flags the missing entry in STATUS_MAP. This is safer than a Map or plain object with string keys.
Extract narrows a discriminated union to specific variants using template literal patterns. Each event handler gets exactly the fields that exist on its event type — no type assertions, no runtime checks. This pattern scales well in event-driven architectures.
ReturnType<typeof fn> and Parameters<typeof fn> let you build wrappers that stay in sync with the original function. If the SDK updates its signature, your wrapper gets a type error instead of a silent runtime bug. Awaited unwraps the Promise so you get the resolved value type.
NoInfer blocks a parameter from contributing to type inference. TypeScript infers T from the first argument only, then checks the second against that result. Without it, TypeScript would widen T to include the default value, defeating the constraint. Use this whenever a "default" or "initial" parameter should match values defined elsewhere.
Assuming `Readonly<T>` freezes nested objects — then mutating a nested array or object and wondering why TypeScript didn't catch it
`Readonly` is shallow. `Readonly<{ items: string[] }>` prevents reassigning `items` but not calling `items.push()`. For deep immutability, use a recursive `DeepReadonly` type or a library like Immer. In most codebases, shallow `Readonly` on API response types is good enough — deep freezing adds complexity without much practical benefit.
Confusing `Omit`/`Pick` (object properties) with `Exclude`/`Extract` (union members) — trying to use `Exclude` to remove a property from an object type
`Pick` and `Omit` operate on object property keys. `Extract` and `Exclude` filter members of a union type. To remove a property: `Omit<User, "password">`. To remove a union member: `Exclude<"a" | "b" | "c", "a">`. They look similar but work on fundamentally different things.
Passing an instance type to `ReturnType` — writing `ReturnType<MyClass>` instead of `ReturnType<typeof myFunction>`
`ReturnType` expects a function type, not an instance type. For functions: `ReturnType<typeof myFunction>`. For class methods: `ReturnType<MyClass["methodName"]>`. The `typeof` is almost always needed because you're referencing a value, not a type.
Using `Omit` with key names that don't exist on the type — `Omit<User, "pasword">` compiles silently with the typo and does nothing
TypeScript doesn't warn when you `Omit` a non-existent key. If you need safety, create a strict version: `type StrictOmit<T, K extends keyof T> = Omit<T, K>`. This catches typos because K is constrained to actual keys of T. Some teams add this to their shared utility types.
TypeScript's utility types are composable building blocks for type transformations. Partial and Required toggle optionality. Pick and Omit reshape objects. Record builds typed dictionaries. Exclude and Extract filter unions. ReturnType and Parameters extract function signatures. Awaited unwraps Promises. NoInfer controls inference direction. The key skill is not memorizing each one — it's knowing when to compose them. Define your base types once, then derive everything else.
Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.
Interactive lessons and challenges, right in your code editor.
Check the free courses. No credit card.