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

💡 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

UtilityDoesExample
Partial<T>All fields optionalPartial<User>
Required<T>All fields requiredRequired<Config>
Readonly<T>All fields read-onlyReadonly<User>
Pick<T, K>Keep only keys KPick<User, "id">
Omit<T, K>Drop keys KOmit<User, "pw">
Record<K, V>Dictionary K→VRecord<string, number>
Exclude<T, U>Remove U from unionExclude<Role, "guest">
Extract<T, U>Keep U from unionExtract<Role, "admin">
ReturnType<F>Type a fn returnsReturnType<typeof f>
Parameters<F>Tuple of fn argsParameters<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)

Pro Tips

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

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

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.

Related lessons