Utility & Mapped Types
Pick, Omit, Partial, Record — deriving types instead of duplicating them.
35 minDifficulty 3/5typesAI-writtenWritten by a model on 16 August 2026 and not yet read by a person. Checked automatically: schema, the pedagogical rules the hand-written material is held to, and every diagram parsed for real.
Before this
Why this exists
A `User` interface has ten fields, and you need a type for "just the fields needed to create a new user" (no `id`, no `createdAt` — the server assigns those), and another for "a user update" (every field optional, since you might only change one). Writing two entirely separate interfaces means all three definitions drift out of sync the next time someone adds a field. Utility types derive the second and third shapes FROM the first, so they can never drift.
The mental model
Utility types are functions, but for types — they take a type as input and produce a new, related type as output, computed by the compiler. `Partial<User>` doesn't duplicate User's fields by hand; it mechanically transforms whatever User currently is into a version with every field optional, so it automatically stays correct as User itself changes.
How it works
Pick and Omit select or exclude specific fields
`Pick<User, 'id' | 'name'>` produces a type with ONLY those two fields from User. `Omit<User, 'id'>` produces a type with every field EXCEPT `id`. Both stay automatically correct if User's other fields change shape — only the field names listed need updating if a field is renamed, never the derived type's own definition.
Partial and Required flip every field's optionality at once
`Partial<User>` makes every field optional — ideal for an update payload where any subset of fields might be provided. `Required<User>` does the reverse, making every optional field mandatory — useful for a fully-validated, guaranteed-complete version of a type that started out with some optional fields.
Record builds an object type with a specific key and value shape
`Record<string, number>` describes an object where every key is a string and every value is a number — like a dictionary or map type. `Record<'admin' | 'user', Permission[]>` goes further, constraining the ALLOWED keys to exactly those two literal strings, catching a typo'd key at compile time that a plain object type wouldn't.
Mapped types are what these utilities are built from, and you can write your own
`Partial<T>` is itself defined as `{ [K in keyof T]?: T[K] }` — a mapped type that iterates over every key of T and makes each one optional. Understanding this pattern means you're not limited to the built-in utilities; a custom mapped type like `{ [K in keyof T]: T[K] | null }` (making every field nullable) follows the exact same structure.
The mechanism
A utility type is a generic type alias that transforms its input type parameter using TypeScript's type-level operations — mapped types (iterating over keys), conditional types, and key remapping. The compiler evaluates this transformation once for each concrete type it's applied to, producing a new type shape that automatically reflects any later change to the original type, since it's computed from it rather than copied.
What people get wrong
- Pick, Omit, Partial, and Record are special compiler features that can't be replicated in user code.
- They're ordinary generic mapped types defined in TypeScript's own standard library, using the same mapped-type syntax available to any developer — nothing about them is a compiler-only special case. Understanding that they're 'just' mapped types is what unlocks writing custom equivalents for patterns the built-ins don't cover, rather than treating them as fixed, unextendable primitives.
- Duplicating a type manually (writing out a second interface with a subset of fields) is equivalent to using Pick or Omit.
- A manually duplicated interface has no connection to the original — renaming or changing a field in the original type doesn't propagate to the duplicate, silently leaving it out of sync; a derived type via Pick/Omit is recomputed from the current shape of the original every time. This drift is exactly the bug utility types exist to prevent — a manually maintained 'subset' type is a second source of truth that WILL eventually disagree with the first.
- Partial<T> makes a type's fields optional AND allows them to be null.
- Partial<T> only makes fields optional (`?`, meaning the key can be absent) — it does not add `null` as an allowed value for present fields; a field explicitly set to `null` where the original type didn't allow it is still a type error under Partial. This distinction matters in practice: `undefined` (absent) and `null` (present but empty) are different things, and Partial only addresses the first, not the second.
When not to use it
- The transformation you need doesn't match any built-in utility, like making every field nullable instead of optional.
- Write a custom mapped type following the same pattern, e.g. `type Nullable<T> = { [K in keyof T]: T[K] | null }` — the built-ins are a starting set, not the complete list of possible transformations.
- Two types are genuinely unrelated and happen to share a few field names by coincidence.
- Separate, independently-defined types — deriving one from the other with Pick or Omit would create a false dependency between concepts that aren't actually related.
Terms
- Mapped type
- — A type constructed by iterating over the keys of another type and transforming each property, the mechanism utility types like Partial and Pick are built from.
- Pick
- — A utility type that constructs a new type containing only the specified subset of properties from another type.
- Omit
- — A utility type that constructs a new type containing all properties of another type except the specified ones.
- Record
- — A utility type describing an object type with a specified key type and a uniform value type for every key.
Can you recall it?
Why is `type CreateUserInput = Omit<User, 'id' | 'createdAt'>` better than manually writing out a second, separate interface with the same fields minus those two?