Utility Types (Partial, Pick, Omit, Record)
Reviewed & published by Brayan K
Utility types are built-in generic helpers like Partial, Pick, Omit, and Record that transform one type into a related type — making fields optional, selecting a subset of keys, or describing a dictionary — so you never have to copy-paste and edit an interface by hand.
Part of the free TypeScript course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn
- Make every field optional with Partial<T> (and required with Required<T>)
- Lock a type down with Readonly<T>
- Select a subset of keys with Pick<T, K> and drop keys with Omit<T, K>
- Describe dictionaries and lookup tables with Record<K, V>
- Filter unions with Exclude<T, U> and Extract<T, U>
- Pull a function's return type and arguments with ReturnType<F> and Parameters<F>
💡 Real-World Analogy
Think of a User interface as a master blueprint for a house. Utility types are the variant plans you stamp out from it without redrawing anything. Partial<User> is the "renovation checklist" where any room can be left blank. Pick<User, "id" | "name"> is the "front-door nameplate" showing only a couple of details. Omit<User, "password"> is the "public listing" with the private bits removed. Record<Room, Furniture> is a "room-by-room inventory". One blueprint, many derived plans — and they all stay in sync because they're computed from the original.
📊 The Utility Types at a Glance
| Utility | Does | Example |
|---|---|---|
| Partial<T> | All fields optional | Partial<User> |
| Required<T> | All fields required | Required<Config> |
| Readonly<T> | All fields read-only | Readonly<User> |
| Pick<T, K> | Keep only keys K | Pick<User, "id"> |
| Omit<T, K> | Drop keys K | Omit<User, "pw"> |
| Record<K, V> | Dictionary K→V | Record<string, number> |
| Exclude<T, U> | Remove U from union | Exclude<Role, "guest"> |
| Extract<T, U> | Keep U from union | Extract<Role, "admin"> |
| ReturnType<F> | Type a fn returns | ReturnType<typeof f> |
| Parameters<F> | Tuple of fn args | Parameters<typeof f> |
1. Partial, Required, Readonly, Pick & Omit
Start with one source type and derive the rest. Partial<T> makes every property optional — perfect for an "update" payload where the caller only sends the fields that changed. Required<T> does the reverse. Readonly<T> stops reassignment after construction. Pick<T, K> keeps only the keys you list, and Omit<T, K> keeps everything except them. Here is the full type-level picture:
interface User {
id: number;
name: string;
email: string;
admin: boolean;
}
// Partial — every field becomes optional (great for updates/patches)
type UserUpdate = Partial<User>;
// => { id?: number; name?: string; email?: string; admin?: boolean }
// Required — force every field to be present (the inverse of Partial)
type StrictUser = Required<UserUpdate>; // back to all-required
// Readonly — no reassignment after creation
type FrozenUser = Readonly<User>;
// user.name = "x" // ❌ Cannot assign to 'name' because it is read-only
// Pick — keep ONLY the listed keys
type UserSummary = Pick<User, "id" | "name">;
// => { id: number; name: string }
// Omit — keep everything EXCEPT the listed keys
type PublicUser = Omit<User, "email">;
// => { id: number; name: string; admin: boolean }The runnable example below shows the data these types describe: a full object, a patch merged over it (Partial), a picked subset (Pick), and an omitted-key version (Omit).
// === The runtime DATA that utility types describe ===
// A "full" user record we'll reshape with utility types like Partial, Pick, Omit.
const fullUser = { id: 1, name: "Alice", email: "alice@example.com", admin: false };
console.log("Full user:", fullUser);
// Full user: { id: 1, name: 'Alice', email: 'alice@example.com', admin: false }
// Partial<User> describes an object where EVERY field is optional.
// At runtime, that's just an object with SOME of the keys — like a patch:
const patch = { name: "Alice B." }; // only the fields we want to change
const updated = { ...fullUser, ...patch }; // merge patch over the original
console.log("After patch:", updated);
// After patch: { id: 1, name: 'Alice B.', email: 'alice@example.com', admin: false }
// Pick<User, "id" | "name"> describes an object with ONLY those keys:
const summary = { id: fullUser.id, name: fullUser.name };
console.log("Picked summary:", summary); // Picked summary: { id: 1, name: 'Alice' }
// Omit<User, "email"> describes the object WITHOUT that key:
const { email, ...withoutEmail } = fullUser;
console.log("Omitted email:", withoutEmail);
// Omitted email: { id: 1, name: 'Alice', admin: false }2. Record — Dictionaries & Lookup Tables
Record<K, V> describes an object whose keys are of type K and whose values are all of type V. With Record<string, number> you get an open dictionary (any string key, number value). With a union of literals as the key — Record<"red" | "green" | "blue", string> — the compiler insists every one of those exact keys is present, which is how you build a guaranteed-complete lookup table.
// Open dictionary: any string key -> a number value
type WordCount = Record<string, number>;
const counts: WordCount = { apples: 3, pears: 7 };
// Fixed key set: EVERY color must be defined, value is a hex string
type Color = "red" | "green" | "blue";
const hexes: Record<Color, string> = {
red: "#ff0000",
green: "#00ff00",
blue: "#0000ff", // miss one and TS errors: Property 'blue' is missing
};
// Record is itself just a mapped type under the hood:
// type Record<K extends keyof any, V> = { [P in K]: V };// === Record<K, V> — an object used as a lookup table / dictionary ===
// Record<string, number> describes "an object whose keys are strings
// and whose values are all numbers". At runtime that's just an object:
const wordCount = { apples: 3, pears: 7, plums: 1 };
console.log("Count for pears:", wordCount.pears); // Count for pears: 7
// Building a Record from a list — tally how many times each fruit appears:
const basket = ["apple", "pear", "apple", "plum", "pear", "apple"];
const tally = {}; // Record<string, number>
for (const fruit of basket) {
tally[fruit] = (tally[fruit] || 0) + 1;
}
console.log("Tally:", tally); // Tally: { apple: 3, pear: 2, plum: 1 }
// Record<"red" | "green" | "blue", string> — a fixed set of keys.
// Every key MUST be present (unlike Partial). At runtime, a full object:
const hexes = { red: "#ff0000", green: "#00ff00", blue: "#0000ff" };
console.log("Green is", hexes.green); // Green is #00ff00
console.log("All colors:", Object.keys(hexes)); // All colors: [ 'red', 'green', 'blue' ]3. Filtering Unions & Reading Functions
The last group works on unions and functions. Exclude<T, U> removes the members of U from union T; Extract<T, U> keeps only the members assignable to U. ReturnType<F> gives you whatever a function returns, and Parameters<F> gives you a tuple of its argument types — so you can derive types from a function instead of duplicating them.
type Role = "guest" | "user" | "admin" | "owner";
// Exclude — drop members from a union
type WriteRole = Exclude<Role, "guest">; // "user" | "admin" | "owner"
// Extract — keep only matching members
type Privileged = Extract<Role, "admin" | "owner">; // "admin" | "owner"
function createUser(name: string, age: number) {
return { id: Date.now(), name, age, active: true };
}
// ReturnType — the object type createUser returns, derived automatically
type NewUser = ReturnType<typeof createUser>;
// => { id: number; name: string; age: number; active: boolean }
// Parameters — a tuple of the argument types
type CreateArgs = Parameters<typeof createUser>; // [string, number]// === ReturnType<F> and Parameters<F> — types EXTRACTED from a function ===
// These utilities read a function's shape. At runtime, the function and its
// actual return value / arguments are what those types describe.
function createUser(name, age) {
return { id: Date.now(), name, age, active: true };
}
// Parameters<typeof createUser> is the TUPLE type [string, number].
// At runtime that's exactly the argument list you pass:
const args = ["Alice", 30];
const user = createUser(...args);
console.log("Made from args:", { name: user.name, age: user.age });
// Made from args: { name: 'Alice', age: 30 }
// ReturnType<typeof createUser> is the type of whatever createUser RETURNS,
// so you never re-type the shape by hand. The runtime value:
console.log("Return keys:", Object.keys(user));
// Return keys: [ 'id', 'name', 'age', 'active' ]
// Exclude<T, U> / Extract<T, U> filter a union of string literals.
// At runtime those literals are just values in an array:
const allRoles = ["guest", "user", "admin", "owner"];
const writeRoles = allRoles.filter((r) => r !== "guest"); // "Exclude guest"
console.log("Can write:", writeRoles); // Can write: [ 'user', 'admin', 'owner' ]
const privileged = allRoles.filter((r) => r === "admin" || r === "owner"); // "Extract"
console.log("Privileged:", privileged); // Privileged: [ 'admin', 'owner' ]🎯 Your Turn
Fill in the three blanks below. Each one mirrors a utility type at runtime: a Partial-style patch, a Pick-style subset, and an Omit via destructuring. Run it and match the expected output.
// 🎯 YOUR TURN — replace each ___ then press "Try it Yourself".
// We model the same idea utility types describe, at runtime.
const product = { id: 101, title: "Coffee Mug", price: 9.5, inStock: true };
// 1) A PATCH object (this is what Partial<Product> allows) — change ONLY price.
let patch = ___; // 👉 e.g. { price: 7.99 }
let updated = { ...product, ...patch };
console.log("updated:", updated);
// 2) A "Pick" summary — an object with ONLY id and title.
let summary = ___; // 👉 e.g. { id: product.id, title: product.title }
console.log("summary:", summary);
// 3) "Omit" inStock using destructuring rest.
let { inStock, ...withoutStock } = ___; // 👉 which object are we omitting from?
console.log("withoutStock:", withoutStock);
// ✅ Expected output (example):
// updated: { id: 101, title: 'Coffee Mug', price: 7.99, inStock: true }
// summary: { id: 101, title: 'Coffee Mug' }
// withoutStock: { id: 101, title: 'Coffee Mug', price: 9.5 }Common Errors (and the fix)
- ❌ Passing a non-key to Pick/Omit: Pick<User, "naem"> → "Type '"naem"' does not satisfy the constraint 'keyof User'". ✅ The second argument must be real keys of the type — fix the typo to "name".
- ❌ Thinking Partial validates at runtime: Partial<User> does not stop a wrong-typed value from reaching you over the network — it only types the variable. ✅ Validate untrusted input (e.g. with a schema library) then treat it as the typed shape.
- ❌ Forgetting a required Record key: with Record<Color, string>, leaving out blue gives "Property 'blue' is missing". ✅ Either supply every key or use Partial<Record<Color, string>> if some may be absent.
- ❌ Mutating a Readonly: const u: Readonly<User>; u.name = "x" → "Cannot assign to 'name' because it is a read-only property". ✅ Build a new object with the spread syntax instead: { ...u, name: "x" }.
- ❌ Using Exclude on objects: Exclude/Extract filter unions, not object properties. ✅ To drop a property use Omit; to drop a union member use Exclude.
Pro Tips
- 💡 Derive, don't duplicate: build "update", "create", and "summary" types from one source interface with Partial/Omit/Pick so they can never drift apart.
- 💡 Nest utilities freely: Partial<Pick<User, "name" | "email">> is a perfectly normal type.
- 💡 Prefer Omit for "create" payloads (everything minus the server fields) and Pick for narrow read views.
- 💡 Record with a literal union guarantees an exhaustive lookup table — the compiler flags any missing key.
Mini-Challenge: Settings Updater
Build a small settings updater that exercises Partial (the merge), Pick (the public view), and Omit (dropping a key) all at runtime. Follow the outline, run it, and match the example output.
// 🎯 MINI-CHALLENGE: a settings updater
// Utility types in TS: Settings, Partial<Settings>, Pick<Settings,...>, Omit<Settings,...>
//
// 1. Start from a full settings object:
// { theme: "dark", fontSize: 14, autosave: true, language: "en" }
// 2. Write applyDefaults(base, overrides) that merges a PARTIAL "overrides"
// object over "base" (overrides win). Return the merged object.
// 3. Call it with overrides { fontSize: 18, theme: "light" } and log the result.
// 4. Build a "public view" by PICKING only theme and fontSize.
// 5. Build a "no language" object by OMITTING the language key.
//
// ✅ Example output:
// merged: { theme: 'light', fontSize: 18, autosave: true, language: 'en' }
// public: { theme: 'light', fontSize: 18 }
// no language: { theme: 'light', fontSize: 18, autosave: true }
// your code here🎉 Lesson Complete
- ✅ Partial<T>/Required<T> flip every field optional/required; Readonly<T> freezes them
- ✅ Pick<T, K> keeps only listed keys; Omit<T, K> drops them — ideal for views and create payloads
- ✅ Record<K, V> types dictionaries; a literal-union key demands an exhaustive table
- ✅ Exclude<T, U>/Extract<T, U> filter union members
- ✅ ReturnType<F>/Parameters<F> derive types straight from a function
- ✅ Utility types are compile-time only — zero runtime cost, pure descriptions of your data
Practice quiz
Partial<T> makes every property of T:
- Required
- Readonly
- Optional
- A string
Answer: Optional. Partial<T> marks every property optional — ideal for an update or patch shape.
Required<T> is the inverse of:
- Partial
- Pick
- Record
- Omit
Answer: Partial. Required<T> forces every property to be present, undoing Partial.
Pick<User, 'id' | 'name'> produces a type with:
- Everything except id and name
- All keys optional
- A union of id and name
- Only id and name
Answer: Only id and name. Pick<T, K> keeps only the listed keys.
Omit<User, 'email'> produces a type that:
- Has only email
- Has everything except email
- Makes email optional
- Is never
Answer: Has everything except email. Omit<T, K> keeps everything except the named keys — the mirror of Pick.
Record<'red' | 'green' | 'blue', string> requires:
- Every one of those exact keys
- At least one of the keys
- No keys
- Numeric keys
Answer: Every one of those exact keys. With a literal-union key, Record demands every key be present — a complete lookup table.
Exclude<'guest' | 'user' | 'admin', 'guest'> is:
- 'guest'
- 'guest' | 'user' | 'admin'
- 'user' | 'admin'
- never
Answer: 'user' | 'admin'. Exclude<T, U> removes the members of U from the union T.
Extract<'guest' | 'user' | 'admin', 'admin' | 'owner'> is:
- 'guest' | 'user'
- 'admin'
- 'admin' | 'owner'
- never
Answer: 'admin'. Extract<T, U> keeps only the members of T assignable to U — here just 'admin'.
Parameters<typeof f> for f(name: string, age: number) is:
- string | number
- { name: string; age: number }
- never
- [string, number]
Answer: [string, number]. Parameters<F> gives the argument types as a tuple, here [string, number].
Utility types like Partial and Pick at runtime:
- Add validation code
- Are erased — zero runtime cost
- Become classes
- Slow down objects
Answer: Are erased — zero runtime cost. They are compile-time only and produce no JavaScript output.
Record<K, V> is itself implemented as a:
- Conditional type
- Union type
- Mapped type { [P in K]: V }
- Tuple type
Answer: Mapped type { [P in K]: V }. Record<K, V> = { [P in K]: V } — a mapped type under the hood.
Continue this course
- Previous: Checkpoint: The Type System
- Next: Mapped Types — Build new types by transforming the keys of existing ones
- Quick reference: TypeScript cheat sheet › Utility Types
Frequently asked questions
What is the difference between Partial and the optional ? modifier?
They produce the same effect, but at different scope. Writing a property as 'name?: string' marks that ONE property optional when you define the interface. Partial<User> takes an EXISTING type and makes EVERY property optional in one go, without rewriting the interface. Use Partial when you already have a complete type (like a User) and need a 'patch' or 'update' shape where any subset of fields may be present.
When should I use Pick versus Omit?
They are mirror images. Pick<T, Keys> keeps ONLY the keys you name; Omit<T, Keys> keeps everything EXCEPT the keys you name. Reach for Pick when you want a small subset of a large type (e.g. just id and name for a list view). Reach for Omit when you want almost the whole type minus one or two fields (e.g. a 'create' payload that is a User without the server-generated id).
Record<K, V> vs an index signature like { [key: string]: V } — what's the difference?
Record<string, V> compiles to essentially the same thing as { [key: string]: V } for string keys. The real power of Record appears with a UNION of literal keys: Record<'red' | 'green' | 'blue', string> forces an object to define EVERY one of those exact keys — an index signature can't require a specific finite set. Use Record with literal unions when you want the compiler to guarantee a complete lookup table.
Do utility types exist at runtime, or do they cost anything?
They exist only at compile time. Partial, Pick, Omit, Record, ReturnType and friends are erased when TypeScript compiles to JavaScript — they generate zero runtime code and have zero performance cost. They are pure descriptions that help the compiler check your code; the values they describe are just ordinary JavaScript objects, arrays, and functions.