Utility Types

Med

TypeScript ships with built-in utility types that transform existing types into new ones. These let you derive types from other types without duplicating definitions. Understanding utility types is essential because they appear constantly in real codebases and are a staple of TypeScript interviews.

Interactive Visualization

Key Points

  • `Partial<T>` makes all properties of T optional
  • `Required<T>` makes all properties of T required
  • `Pick<T, K>` creates a type with only the specified keys from T
  • `Omit<T, K>` creates a type with all keys except the specified ones
  • `Record<K, V>` creates an object type with keys K and values V
  • `Readonly<T>` makes all properties of T read-only
  • `ReturnType<T>` extracts the return type of a function type

Code Examples

Partial, Required & Readonly

interface User {
  id: number
  name: string
  email: string
  avatar?: string
}

// Partial — all fields become optional
type UpdateUser = Partial<User>
// { id?: number; name?: string; email?: string; avatar?: string }

function updateUser(id: number, updates: Partial<User>): User {
  const existing = getUserById(id)
  return { ...existing, ...updates }
}

// Required — all fields become required (removes ?)
type CompleteUser = Required<User>
// avatar is now required

// Readonly — prevents mutation
type FrozenUser = Readonly<User>
const user: FrozenUser = { id: 1, name: 'Alice', email: 'a@b.com' }
// user.name = 'Bob'  // Error: Cannot assign to 'name'

declare function getUserById(id: number): User

Partial is the most commonly used utility type. It is perfect for update/patch operations where only some fields need to change.

Pick, Omit & Record

interface Product {
  id: string
  name: string
  price: number
  description: string
  category: string
  createdAt: Date
}

// Pick — select specific properties
type ProductSummary = Pick<Product, 'id' | 'name' | 'price'>
// { id: string; name: string; price: number }

// Omit — remove specific properties
type ProductInput = Omit<Product, 'id' | 'createdAt'>
// { name: string; price: number; description: string; category: string }

// Record — create an object type from keys and value type
type CategoryCounts = Record<string, number>
const counts: CategoryCounts = {
  electronics: 42,
  clothing: 18,
}

// Record with union keys
type StatusMap = Record<'active' | 'inactive' | 'pending', User[]>

Pick and Omit are complementary — one selects properties, the other excludes them. Record is ideal for dictionary-like objects with known key patterns.

Extract, Exclude & ReturnType

type EventType = 'click' | 'scroll' | 'mousemove' | 'keydown' | 'keyup'

// Extract — keep only members that match
type MouseEvents = Extract<EventType, 'click' | 'scroll' | 'mousemove'>
// 'click' | 'scroll' | 'mousemove'

// Exclude — remove members that match
type KeyboardEvents = Exclude<EventType, 'click' | 'scroll' | 'mousemove'>
// 'keydown' | 'keyup'

// ReturnType — extract return type of a function
function createUser(name: string, age: number) {
  return { id: crypto.randomUUID(), name, age, createdAt: new Date() }
}

type NewUser = ReturnType<typeof createUser>
// { id: string; name: string; age: number; createdAt: Date }

// Parameters — extract parameter types as a tuple
type CreateUserParams = Parameters<typeof createUser>
// [name: string, age: number]

Extract and Exclude operate on union types. ReturnType and Parameters operate on function types. These are powerful for deriving types from existing code without duplication.

Common Mistakes

  • Redefining types manually when a utility type would derive it from an existing type
  • Confusing Pick (select properties) with Extract (filter union members)
  • Using `Omit<T, "key">` without realizing it does not error on non-existent keys
  • Forgetting that `Readonly` is shallow — nested objects remain mutable
  • Not knowing that `Partial` makes ALL properties optional, including required ones

Interview Tips

  • Be ready to explain the difference between Pick and Omit with real examples
  • Know how to implement Partial, Pick, and Readonly from scratch using mapped types
  • Mention that utility types are built on mapped types and conditional types under the hood
  • Show fluency by combining utility types: `Partial<Pick<User, "name" | "email">>`

Related Concepts