TypeScript Utility Types: Partial, Pick, Omit, Record, and More
- typescript
- javascript
- utility-types
- frontend
- backend
TypeScript ships with a set of built-in utility types — Partial, Pick, Omit, Record, and a dozen more — that transform one type into another. They look like magic the first time you see them, but they are ordinary generics written in the type system's own language, and learning them pays off immediately because they let you derive types from other types instead of hand-writing every variation. The rule they encourage is one worth adopting as a principle: define a type once, and derive everything else from it.
This is part of the TypeScript type system series. Here are the utility types you will actually use, grouped by what they do.
Why derive instead of duplicate
Consider a User type and the handful of related shapes an app needs: the fields required to create a user, the fields allowed in an update, the shape returned by the API without the password. The tempting approach is to write each as its own interface. The problem is drift — add a field to User and you now have to remember to update three other interfaces by hand, and the day you forget is the day a bug ships.
Utility types remove the duplication. You define User as the single source of truth and compute the variations from it. When User changes, every derived type updates automatically, because they are defined in terms of it. This is the core habit: one canonical type, many derivations.
Reshaping which properties exist
These four are the workhorses, and between them they cover most day-to-day needs.
Partial<T> makes every property optional. Its signature use is update operations, where the caller supplies only the fields they want to change:
function updateUser(id: string, changes: Partial<User>) { /* ... */ }
updateUser("1", { name: "New Name" }) // valid — other fields omitted
Required<T> is the inverse, making every property required — useful when you have a config type with optional fields that becomes fully populated after applying defaults.
Pick<T, Keys> builds a new type from a subset of another's properties. You keep only the keys you name:
type UserPreview = Pick<User, "id" | "name" | "avatar">
Omit<T, Keys> is the opposite — take everything except the named keys. It is the natural way to express "the same shape, minus the sensitive or server-controlled fields":
type PublicUser = Omit<User, "password" | "email">
type CreateUser = Omit<User, "id" | "createdAt"> // id and timestamp set by the server
Pick and Omit are two framings of the same operation, and you choose whichever makes the intent clearer. If you are keeping a few fields out of many, Pick. If you are dropping a few from many, Omit. These are exactly the types you want when defining NestJS DTOs — a create DTO is often Omit of the server-generated fields, an update DTO a Partial of that.
Building map-like types
Record<Keys, Value> constructs an object type with a known set of keys, all sharing a value type. It is the clean way to type a lookup or dictionary:
type Role = "admin" | "editor" | "viewer"
type Permissions = Record<Role, boolean>
// { admin: boolean; editor: boolean; viewer: boolean }
Because the keys come from a union, Record also gives you exhaustiveness: if Role gains a fourth value, any Record<Role, ...> object is now missing a key and fails to typecheck until you add it. That is a free correctness check you get just by deriving the type from the union.
Extracting from unions and functions
A second family operates on unions and function types, pulling pieces out of existing types.
Exclude<T, U> removes members from a union; Extract<T, U> keeps only the matching ones:
type Status = "active" | "inactive" | "banned"
type Allowed = Exclude<Status, "banned"> // "active" | "inactive"
NonNullable<T> strips null and undefined from a type, which is handy after you have narrowed away the empty cases.
ReturnType<T> and Parameters<T> extract the return type and parameter types of a function type. These are more advanced but genuinely useful for staying in sync with a function you do not control:
function createUser(name: string, age: number) {
return { id: "1", name, age }
}
type NewUser = ReturnType<typeof createUser> // { id: string; name: string; age: number }
Now NewUser tracks whatever createUser returns. Change the function and the type follows, with no manual edit. This is derivation taken to its logical end: even the shape produced by a function becomes a single source of truth you can reference elsewhere.
They are not magic — you can read them
The thing that turns these from memorized incantations into tools you can reason about is realizing they are written in plain TypeScript. Partial is just a mapped type:
type Partial<T> = { [K in keyof T]?: T[K] }
That reads as "for each key K in T, make a property that is optional and has T's value type for that key." Pick, Record, and the rest are similarly short. You do not need to write your own utility types often, but once you can read the built-in ones, the boundary between "using TypeScript" and "programming with types" dissolves, and complex typing problems become a matter of composing transformations rather than reaching for any.
The habit to keep
Whenever you are about to hand-write a type that is "mostly like this other type but with some fields changed," stop and reach for a utility type instead. Define the canonical shape once, derive the variations, and let changes propagate for free. It is a small discipline that compounds: types that stay correct as the code evolves, without the manual bookkeeping that lets bugs slip in. The utility types are the everyday tools for it, and they lean directly on the generics and mapped types covered elsewhere in this series.
Key takeaways
- Utility types are ordinary generics, not magic; they let you derive related types from one canonical type so changes propagate automatically.
- Partial makes properties optional (updates); Required does the inverse; Pick keeps named properties; Omit drops them (create DTOs, public shapes).
- Record builds a map-like type from a key union and a value type, and gives exhaustiveness when the key union grows.
- Exclude/Extract filter unions, NonNullable strips null and undefined, and ReturnType/Parameters extract from function types.
- Prefer deriving over duplicating: define the canonical shape once and compute variations, so types stay correct as the code evolves.
Frequently asked questions
What is the difference between Pick and Omit?
Both build a new type from a subset of an existing type's properties. Pick keeps only the properties you name; Omit keeps everything except the ones you name. Use Pick when keeping a few of many, Omit when dropping a few from many — whichever reads more clearly.
When should I use Partial?
For operations where only some fields are provided, most commonly updates. A function like updateUser(id, changes: Partial<User>) accepts any subset of the user's fields while still type-checking each one against the canonical User type.
What does Record do?
Record<Keys, Value> constructs an object type with a known set of keys that all share a value type — a typed dictionary. When the keys come from a union, it also enforces exhaustiveness: adding a member to the union makes existing Record objects fail until you add the new key.
Why derive types instead of writing them by hand?
To avoid drift. If create, update, and public variants of a type are written separately, changing the base type means remembering to update each one, and the day you forget a bug ships. Deriving them with utility types means every variation updates automatically when the canonical type changes.
Further reading
Explore more on

About the author
I'm Aman Kumar Singh, a software engineer in Noida, India building scalable full-stack products with React, Next.js, Node.js, NestJS, PostgreSQL, Redis, and AWS. I write about backend engineering, distributed systems, and system design.