Utility Types
Built-in generic types that transform an existing type into a new one, so you don't have to redefine similar shapes by hand.
What is it?
Once you have an interface like User, you'll often need slightly different versions of it in different situations — a version where every field is optional (for an update form), a version with only a couple of fields (for a preview card), a version missing one sensitive field (to send to the client). Rewriting a near-duplicate interface for each of these is repetitive, and the copies can drift out of sync as the original changes.
TypeScript ships a set of built-in utility types — generic types that take an existing type and transform it into a new one — to cover these common transformations without hand-written duplication. Four of the most used are: Partial<T> (makes every property optional), Pick<T, Keys> (keeps only the listed properties), Omit<T, Keys> (keeps everything except the listed properties), and Record<Keys, ValueType> (builds an object type mapping each key to the same value type).
Explain like I'm 10
Utility types are like photocopier settings for a type. Instead of retyping a document by hand with a few fields blanked out, you run the original through a setting — "make every field optional," "keep only these two fields," "drop that one field" — and get a related but different copy out, without ever hand-editing the original.
Examples
Partial, Pick, and Omit
interface User {
id: string;
name: string;
email: string;
age: number;
}
// Every property becomes optional — handy for an "update" function
type UserUpdate = Partial<User>;
const update: UserUpdate = { name: "New Name" }; // valid, other fields omitted
// Keep only name and email
type UserPreview = Pick<User, "name" | "email">;
const preview: UserPreview = { name: "Kai", email: "kai@example.com" };
// Keep everything except id (e.g. before the id is assigned)
type NewUser = Omit<User, "id">;
const draft: NewUser = { name: "Sam", email: "sam@example.com", age: 22 };Each utility type derives a new shape from User without duplicating its definition. If User gains or loses a field later, UserUpdate, UserPreview, and NewUser all stay automatically in sync.
Record for building object types
type Role = "admin" | "editor" | "viewer";
// An object type with exactly these three keys, each mapping to a boolean
type RolePermissions = Record<Role, boolean>;
const permissions: RolePermissions = {
admin: true,
editor: true,
viewer: false,
};
// Missing a key is an error:
const incomplete: RolePermissions = { admin: true, editor: true };
// Error: Property 'viewer' is missing.Record<Keys, ValueType> builds an object type where every key in Keys maps to ValueType. It's especially useful for lookup tables keyed by a union of specific string values.
How it works
Utility types aren't special syntax — they're ordinary generic types defined using features like mapped types (covered later) that ship built into TypeScript's standard library of type definitions. For example, Partial<T> is implemented internally as a mapped type that iterates over every property key in T and re-adds it with a ?. When you write Partial<User>, the compiler expands that definition using User in place of T, producing the equivalent optional-everything type. They only affect compile-time checking — like interfaces and type aliases, they leave no trace in the compiled JavaScript.
Why does it exist?
Deriving a related-but-different type from an existing one is an extremely common need, and utility types cover the most frequent patterns (optional versions, subsets, everything-except, lookup tables) so you don't have to hand-write and maintain nearly-duplicate interfaces that can silently drift apart from the original over time.
When to use it
Reach for Partial when building update or patch functions where any subset of fields may be provided, Pick/Omit when you need a narrower or almost-complete view of an existing type, and Record when building a lookup table or map keyed by a known, fixed set of values.
When not to use it
If the derived type's shape is meant to diverge significantly from the original — different property names, genuinely unrelated structure — a fresh interface is clearer than forcing a utility type transformation onto an unrelated shape. Utility types shine specifically when the new type is a direct derivative of an existing one.
Common mistakes
Using
Partial<T>and assuming it makes properties allowed to benull— it only makes them optional (possibly omitted), not nullable.Passing a key to
PickorOmitthat doesn't actually exist on the source type, which TypeScript flags as an error rather than silently ignoring.Reaching for
Record<string, T>when the keys are actually a known, fixed set — using the specific union of keys instead gives you far better checking (catching missing or misspelled keys).
Practice exercises
- Easy:
Given an interface
Product { id: string; name: string; price: number; }, create aPartial<Product>type and an object using it that only setsprice. - Medium:
Using the same
Productinterface, create aProductSummarytype withPickthat keeps onlynameandprice, and aProductWithoutIdtype withOmitthat dropsid. - Hard:
Create a type
Weekdayas a union of the five weekday names, then useRecord<Weekday, number>to build a type representing hours worked each day, and construct a valid object of that type.
Interview questions
What does `Partial<T>` do to each property of T, mechanically?
It's a mapped type that iterates every key K in keyof T and re-adds it with a ? modifier, so each property becomes optional — the object may omit it entirely, but any property that IS present must still match its original type.
How would you implement `Partial<T>` yourself as a mapped type?
type MyPartial<T> = { [K in keyof T]?: T[K] }; — iterate every key of T and copy it back with ? added, leaving the value type itself untouched.
What is `Required<T>`, and how does it invert what `Partial<T>` does?
Required<T> produces a version of T where every property is mandatory, even ones that were originally optional — it's the mirror image of Partial<T>, which adds optionality instead of removing it.
How is `Required<T>` implemented under the hood, and what does the `-?` syntax do?
type Required<T> = { [K in keyof T]-?: T[K] }; — the -? modifier explicitly strips any existing ? from each property, forcing it to be present, the opposite of the ? that Partial adds.
Given `type Foo = Required<Partial<User>>;`, what shape does `Foo` end up with?
The same shape as User with every property required again — Partial first makes every property optional, then Required strips that optionality back off, so the two operations cancel out.
Does `Partial<T>` make a property nullable, i.e. assignable to `null`?
No — ? only means the property may be omitted from the object entirely; it says nothing about the property's value type accepting null. A property typed string under Partial becomes optional string, not string | null, unless null was already part of the original type.
What does `Pick<T, K>` do?
It builds a new type containing only the properties named in K, dropping every other property that T has.
What does `Omit<T, K>` do, and how does it relate to `Pick`?
It keeps every property of T except the ones named in K — the mirror image of Pick, which keeps only the named ones.
How is `Omit<T, K>` actually implemented internally in terms of `Pick` and `Exclude`?
As Pick<T, Exclude<keyof T, K>> — it first computes the remaining keys by removing K from keyof T using Exclude, then hands that leftover key union to Pick, which is why Omit is really just Pick applied to a computed complement of keys.
Why does passing a key that doesn't exist on T produce a compile error with `Pick<T, K>` but not with `Omit<T, K>`?
Pick's type parameter is constrained as K extends keyof T, so an unknown key fails that constraint outright. Omit's type parameter is only constrained as K extends keyof any (any property key at all), not keyof T, so passing a key T doesn't have is accepted — Exclude<keyof T, K> simply has nothing to remove for that key, with no effect and no error.
What does "distributive" mean for a conditional type like `Exclude`, and why does `Omit` depend on it?
Exclude<A, B> is defined as A extends B ? never : A; when A is a union (like a union of keys), TypeScript checks the condition separately against each member of that union rather than the union as a whole — that per-member checking is what "distributive" means. Omit relies on this: Exclude<keyof T, K> must remove exactly the matching keys one at a time out of potentially many keys in keyof T.
You need a type with every field of `User` except `id`. Would `Omit<User, "id">` or `Pick<User, "name"|"email"|"age">` be the better choice if `User` later gains a new field?
Omit<User, "id"> — it automatically includes any newly added field on User since it only names what to remove. Pick with an explicit key list would silently leave the new field out until someone remembers to add it to the list by hand.
What does `Record<K, V>` do?
It builds an object type where every key in the union K maps to the same value type V — effectively a lookup table with a known, fixed set of keys.
Given `Record<Role, boolean>` for `Role = "admin"|"editor"|"viewer"`, why does an object missing the `viewer` key fail to compile?
Record expands to a mapped type that enumerates every literal in the Role union as a required key, so it behaves exactly like a hand-written interface requiring all three keys — omitting any one of them is a missing-property error, the same as it would be for a plain interface.
Why is `Record<string, T>` a weaker choice than `Record<SpecificKeyUnion, T>` when the keys are actually a small, fixed set?
Record<string, T> expands to an index signature ({ [key: string]: T }) rather than a set of enumerated required keys, so it accepts any string key at all — misspelled or extra keys go unnoticed, and no specific key is ever actually guaranteed present, unlike Record over a literal union, which enumerates and requires each exact key.
How is `Record<K, V>` implemented as a mapped type?
type Record<K extends keyof any, V> = { [P in K]: V }; — it iterates every member of the key union K and assigns each one the same value type V.
What does `Readonly<T>` do?
It's a mapped type that adds the readonly modifier to every property of T, so none of them can be reassigned after the object is created.
Does `Readonly<T>` enforce that immutability at runtime?
No — like every utility type, it's a compile-time-only check. The compiler blocks a reassignment expression from type-checking, but nothing about the compiled JavaScript object itself prevents its properties from being changed, e.g. by code that bypasses the type checker.
Given `const u: Readonly<User> = {...}; u.name = "New";`, why does the assignment fail to compile?
Readonly<User> marks name (and every other property) readonly, and assigning to a readonly property outside of its initial object literal is rejected regardless of the fact that the value being assigned is a valid string.
Is `Readonly<T>`'s restriction "deep" — does it also make properties of a nested object property read-only?
No — Readonly<T> is shallow. It only stops reassigning T's own top-level properties; if one of those properties is itself an object, that nested object's own properties remain fully mutable unless you write a separate, recursive deep-readonly type.
What does `ReturnType<T>` extract, and from what kind of type?
Given a function type, it extracts that function's return type as a standalone type — ReturnType<() => string> is string.
How is `ReturnType<T>` implemented, and what role does `infer` play?
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; — the conditional type checks that T is callable, and infer R captures whatever return type that call signature has, making R available as the result on the true branch.
Does `ReturnType<T>` distribute when T is a union of function types, e.g. `ReturnType<(() => string) | (() => number)>`?
Yes — T appears bare (not wrapped in something else) in the conditional's check, which makes it a distributive conditional type; TypeScript checks each union member separately and unions the results, giving string | number here rather than computing one merged return type.
Does `Partial<T>` distribute the same way over a union type, e.g. `Partial<A | B>`?
No — Partial is a mapped type over keyof T, not a conditional type with T appearing bare in an extends check, so it doesn't distribute. keyof (A | B) only includes keys common to every member of the union, so Partial<A | B> ends up built from just those shared keys — a different, narrower result than Partial<A> | Partial<B>.
Why would you write `ReturnType<typeof someFunction>` instead of naming the return type directly?
When the function's return value's shape isn't given its own named type (e.g. it returns an inline object literal type), ReturnType<typeof someFunction> lets you reference that shape without retyping it by hand, and it stays automatically in sync if the function's return type ever changes.
What type does `Pick<Partial<User>, "name">` produce?
{ name?: string } — Partial<User> first makes every property of User optional, and Pick then keeps only name from that already-optional version, so the result stays optional.
Are `Partial<Pick<User, "name">>` and `Pick<Partial<User>, "name">` equivalent here?
Yes — both end up as { name?: string }. Picking a single key and then making it optional produces the same result as making everything optional and then picking that one key, since there's no interaction between unrelated keys for either order to disturb.
Why do utility types exist as TypeScript built-ins instead of being something every project defines from scratch?
Deriving a related-but-different shape from an existing type (an optional version, a subset, an everything-except version, a lookup table) is an extremely common need across virtually every codebase, so shipping these as a shared standard vocabulary avoids each project re-inventing (and subtly re-implementing) the same handful of mapped/conditional types.
Do utility types like `Partial` and `Pick` leave any trace in the compiled JavaScript output?
No — like interfaces and type aliases, they only affect compile-time type checking. They're expanded and erased entirely by the compiler; nothing about Partial or Pick exists in the emitted JavaScript.
You need a type for a PATCH endpoint that accepts any subset of `User`'s fields except `id`, which should never be updatable. How would you compose utility types to express that?
Partial<Omit<User, "id">> — Omit first removes id from the allowed shape entirely, and Partial then makes every one of the remaining fields optional, so callers may supply any subset of the updatable fields and can never supply id at all.
What's the downside of hand-writing a separate interface `UserPreview { name: string; email: string }` instead of `Pick<User, "name"|"email">`?
The hand-written version has no structural connection to User — if User's name or email field ever changes type, UserPreview silently keeps its old, now-inconsistent definition, whereas Pick<User, ...> re-derives itself from User automatically every time it's used.