Utility Types
MedTypeScript 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">>`