Utility Types (Partial, Pick, etc.)

Difficulty: Intermediate

Question

Explain the most commonly used built-in utility types in TypeScript. When and why would you use each one?

Answer

TypeScript ships with a rich set of utility types that transform existing types without creating new ones from scratch. They reduce boilerplate and keep types in sync with their source.

Property modifiers: Partial<T>, Required<T>, Readonly<T> Property selection: Pick<T,K>, Omit<T,K> Object creation: Record<K,V> Union filtering: Extract<T,U>, Exclude<T,U>, NonNullable<T> Function inspection: ReturnType<T>, Parameters<T>, ConstructorParameters<T>, InstanceType<T> String manipulation: Uppercase<S>, Lowercase<S>, Capitalize<S>, Uncapitalize<S>

Knowing these avoids reinventing the wheel and signals TypeScript maturity to interviewers.

Code examples

Property Modifier Utilities

interface User {
  id: number;
  name: string;
  email: string;
  bio?: string;
}

// Partial - all optional (great for update DTOs)
type UpdateUser = Partial<User>;
// { id?: number; name?: string; email?: string; bio?: string }

function updateUser(id: number, changes: Partial<User>): User {
  const existing = getUserById(id);
  return { ...existing, ...changes };
}
updateUser(1, { name: 'New Name' }); // only update name

// Required - all required (opposite of Partial)
type StrictUser = Required<User>;
// { id: number; name: string; email: string; bio: string }

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

Partial is the most commonly used utility type. It shines for update operations where only some fields change. Required is its inverse, making every field mandatory.

Selection & Creation Utilities

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

// Pick - select specific fields
type ProductCard = Pick<Product, 'id' | 'name' | 'price'>;
// { id: string; name: string; price: number }

// Omit - exclude specific fields
type CreateProduct = Omit<Product, 'id' | 'createdAt'>;
// { name: string; description: string; price: number; category: string }

// Record - create object type from key/value types
type CategoryPrices = Record<string, number>;
const prices: CategoryPrices = {
  electronics: 299,
  books: 19,
  clothing: 49,
};

// Record with literal union keys
type StatusMap = Record<'active' | 'inactive' | 'banned', User[]>;
const users: StatusMap = {
  active: [],
  inactive: [],
  banned: [],
};

Pick and Omit derive subsets of a type. Record creates dictionary-like types. Use literal union keys with Record for exhaustive key coverage.

Union & Function Utilities

// Extract - keep members assignable to U
type T1 = Extract<'a' | 'b' | 'c', 'a' | 'c'>;
// 'a' | 'c'

type T2 = Extract<string | number | boolean, number | boolean>;
// number | boolean

// Exclude - remove members assignable to U
type T3 = Exclude<'a' | 'b' | 'c', 'a'>;
// 'b' | 'c'

// NonNullable - remove null and undefined
type T4 = NonNullable<string | null | undefined>;
// string

// ReturnType - extract function return type
function createUser(name: string, age: number) {
  return { id: Math.random(), name, age, createdAt: new Date() };
}
type NewUser = ReturnType<typeof createUser>;
// { id: number; name: string; age: number; createdAt: Date }

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

// Awaited - unwrap Promise type
type Data = Awaited<Promise<Promise<string>>>;
// string

Extract and Exclude filter union members. ReturnType and Parameters inspect functions without calling them. These are essential for building type-safe wrappers.

Key points

Concepts covered

Partial, Required, Pick, Omit, Record, Extract, Exclude, ReturnType, Parameters