The TypeScript Utility Types You'll Actually Use
Partial, Required, Pick, Omit, Record, Readonly, and the Return/Parameters helpers — what each one does, when to reach for it, and the real-world patterns where they save you from duplicate type definitions.
The short answer: TypeScript's built-in utility types let you derive new types from existing ones instead of hand-writing duplicates. The handful you'll use constantly: Partial<T> (all fields optional), Required<T> (all required), Pick<T, K> / Omit<T, K> (keep or drop fields), Record<K, V> (an object with known keys and a value type), Readonly<T> (immutable), and ReturnType<F> / Parameters<F> (pull types out of functions). Learn these seven and most of your "I need a slightly different version of this type" problems disappear.
Everyone learns interface and type early, then spends a year copy-pasting near-identical type definitions because nobody showed them the utility types. These ship with TypeScript, require no import, and exist precisely to stop you duplicating. Here are the ones that earn their place in daily code. (They're all built on generics and mapped/conditional types — if that machinery is still fuzzy, the day generics clicked is the prerequisite.)
The base type for every example
Partial<T> — make everything optional
The one you'll use most. Perfect for update functions where the caller supplies only the fields that changed.
Required<T> — the inverse
Takes a type with optional fields and makes them all mandatory — handy after you've validated/filled defaults and want to guarantee everything is present downstream.
Pick<T, K> and Omit<T, K> — keep or drop fields
The two you'll reach for whenever you need "the same type but smaller." Pick whitelists; Omit blacklists. Reach for whichever names fewer keys.
Omit<User, "id"> is the canonical "shape for creating a record before the DB assigns the id" — and because it's derived, adding a field to User automatically flows into NewUser. That's the whole point: one source of truth, no drift.
Record<K, V> — a typed dictionary
For an object whose keys you know and whose values share a type. Far cleaner than an index signature when the keys are a known union.
The bonus: if you add "owner" to Role, TypeScript errors until you add its label — the compiler keeps your map exhaustive for free.
Readonly<T> — immutable shapes
Marks every property readonly, so reassignment is a compile error. Good for config objects and anything you pass around that shouldn't be mutated.
ReturnType<F> and Parameters<F> — pull types out of functions
The quiet power tools. Instead of defining a type and a function that returns it, derive the type from the function so they can never drift apart.
This pattern is everywhere in real codebases — typing a Redux action from its creator, a hook's return from the hook, an API response from the fetcher. The function is the source of truth and the type follows.
When NOT to reach for them
Utility types are for deriving related shapes, not for building your core domain types out of transformations nobody can read. If a type needs three nested Omit<Pick<Partial<...>>> to express, you've gone too far — define it directly and keep it legible. The goal is less duplication, not maximum cleverness.
The cheat sheet
| Utility | Does | Reach for it when |
|---------|------|-------------------|
| Partial<T> | all fields optional | update/patch payloads |
| Required<T> | all fields required | after filling defaults |
| Pick<T, K> | keep only listed keys | small view/DTO shapes |
| Omit<T, K> | drop listed keys | create payloads (drop id) |
| Record<K, V> | object keyed by a union | lookups, label maps |
| Readonly<T> | immutable | config, shared data |
| ReturnType<F> | a function's return type | type follows the function |
| Parameters<F> | a function's arg tuple | wrappers, forwarding args |
Learn these seven and you stop writing duplicate type definitions almost entirely. Each one is a way of saying "the same as that, but changed this way" — and keeping types derived from a single source is what makes a large TypeScript codebase refactor-safe instead of a web of definitions that silently drift apart.