TypeScript Utility Types: Partial, Pick, Omit & Record Explained
What Are Utility Types?
TypeScript provides a set of built-in generic types that transform existing types into new ones. These utility types eliminate the need to manually redefine types for common patterns like making properties optional, picking a subset of fields, or creating dictionary types. Instead of duplicating type definitions across your codebase, you derive new types from a single source of truth.
This guide covers every commonly used utility type with real-world examples from API development, React components, and data processing. By the end, you will also know how to build your own custom utility types.
Utility Types at a Glance
| Utility Type | What It Does | Common Use Case |
|---|---|---|
| Partial<T> | All properties optional | Update functions, form state |
| Required<T> | All properties required | Config with defaults merged |
| Readonly<T> | All properties read-only | Immutable state, frozen objects |
| Pick<T, K> | Select specific properties | API response subsets |
| Omit<T, K> | Exclude specific properties | Remove sensitive fields |
| Record<K, V> | Dictionary/map type | Lookup tables, grouped data |
| Exclude<T, U> | Remove types from a union | Filter union members |
| Extract<T, U> | Keep types matching a union | Narrow union types |
| ReturnType<T> | Extract function return type | Infer types from functions |
| NonNullable<T> | Remove null/undefined | Ensure values exist |
Partial<T>: Make Everything Optional
Partial<T> creates a type where every property of T becomes optional. This is the most commonly used utility type, especially for update operations where only some fields change.
interface User {
id: string;
name: string;
email: string;
avatar: string;
role: 'admin' | 'user';
}
// Without Partial -- you'd need a separate type:
// interface UpdateUser { name?: string; email?: string; ... }
// With Partial -- derive it automatically:
type UpdateUser = Partial<User>;
// { id?: string; name?: string; email?: string; avatar?: string; role?: 'admin' | 'user' }
// Real-world usage: update function
async function updateUser(id: string, data: Partial<User>): Promise<User> {
// Only the provided fields are updated
return await db.users.update({ where: { id }, data });
}
// Usage -- only update what changed
await updateUser('user_123', { name: 'New Name' }); // OK
await updateUser('user_123', { email: '[email protected]' }); // OK
await updateUser('user_123', {}); // OK (no changes)
// React form state
const [formData, setFormData] = useState<Partial<User>>({});
function handleChange(field: keyof User, value: string) {
setFormData(prev => ({ ...prev, [field]: value }));
}Pick<T, K> and Omit<T, K>: Select or Exclude Properties
Pick selects specific properties; Omit removes specific properties. They are complementary tools for creating focused type subsets from larger interfaces.
interface User {
id: string;
name: string;
email: string;
password: string;
createdAt: Date;
updatedAt: Date;
}
// Pick: select only what you need
type UserPreview = Pick<User, 'id' | 'name' | 'avatar'>;
// { id: string; name: string; avatar: string }
// Omit: remove what you don't want
type PublicUser = Omit<User, 'password'>;
// { id: string; name: string; email: string; createdAt: Date; updatedAt: Date }
// API layer: never expose password
function getUserProfile(id: string): Promise<PublicUser> {
const user = await db.users.findById(id);
const { password, ...publicUser } = user; // strip password
return publicUser;
}
// Create form: no id or timestamps (server generates those)
type CreateUserInput = Omit<User, 'id' | 'createdAt' | 'updatedAt'>;
// { name: string; email: string; password: string }
// Combine with Partial for flexible update
type UpdateUserInput = Partial<Omit<User, 'id' | 'createdAt'>>;
// { name?: string; email?: string; password?: string; updatedAt?: Date }
// React component props
type UserCardProps = Pick<User, 'name' | 'email'> & {
onClick: () => void;
};Record<K, V>: Dictionary Types
Record<K, V> creates a type with keys of type K and values of type V. It is the type-safe way to define dictionaries, lookup tables, and grouped data.
// Simple dictionary
type UserMap = Record<string, User>;
const users: UserMap = {
'user_1': { id: 'user_1', name: 'Alice', ... },
'user_2': { id: 'user_2', name: 'Bob', ... },
};
// Constrained keys with union type
type Theme = 'light' | 'dark' | 'auto';
type ThemeConfig = Record<Theme, { bg: string; text: string; border: string }>;
const themes: ThemeConfig = {
light: { bg: '#ffffff', text: '#1a1a1a', border: '#e5e5e5' },
dark: { bg: '#0a0a0a', text: '#fafafa', border: '#333333' },
auto: { bg: 'inherit', text: 'inherit', border: 'inherit' },
};
// HTTP status code descriptions
type StatusCode = 200 | 201 | 400 | 401 | 403 | 404 | 500;
const statusMessages: Record<StatusCode, string> = {
200: 'OK',
201: 'Created',
400: 'Bad Request',
401: 'Unauthorized',
403: 'Forbidden',
404: 'Not Found',
500: 'Internal Server Error',
};
// Grouped data
type GroupedByRole = Record<User['role'], User[]>;
const grouped: GroupedByRole = {
admin: [adminUser1, adminUser2],
user: [user1, user2, user3],
};Record types are especially useful when working with JSON data. Format and inspect JSON objects with our JSON Formatter to understand the data shape before defining your Record types.
Exclude<T, U> and Extract<T, U>: Filter Union Types
type Status = 'pending' | 'active' | 'suspended' | 'deleted';
// Exclude: remove members from a union
type ActiveStatus = Exclude<Status, 'deleted' | 'suspended'>;
// 'pending' | 'active'
// Extract: keep only members matching a condition
type InactiveStatus = Extract<Status, 'suspended' | 'deleted'>;
// 'suspended' | 'deleted'
// Real-world: event system
type AppEvent =
| { type: 'click'; x: number; y: number }
| { type: 'keydown'; key: string }
| { type: 'scroll'; offset: number }
| { type: 'resize'; width: number; height: number };
// Extract only mouse-related events
type MouseEvent = Extract<AppEvent, { type: 'click' }>;
// { type: 'click'; x: number; y: number }
// Exclude specific events from handling
type NonScrollEvent = Exclude<AppEvent, { type: 'scroll' }>;
// Extract event types as a union of strings
type EventType = AppEvent['type'];
// 'click' | 'keydown' | 'scroll' | 'resize'ReturnType<T> and Parameters<T>: Infer from Functions
// ReturnType: extract a function's return type
function createUser(name: string, email: string) {
return { id: crypto.randomUUID(), name, email, createdAt: new Date() };
}
type NewUser = ReturnType<typeof createUser>;
// { id: string; name: string; email: string; createdAt: Date }
// Parameters: extract function parameter types as a tuple
type CreateUserParams = Parameters<typeof createUser>;
// [name: string, email: string]
// Real-world: wrapping third-party functions
import { fetchData } from 'some-library';
type FetchResult = ReturnType<typeof fetchData>;
type FetchArgs = Parameters<typeof fetchData>;
// Create a wrapper with the same signature
function cachedFetch(...args: FetchArgs): FetchResult {
const cacheKey = JSON.stringify(args);
if (cache.has(cacheKey)) return cache.get(cacheKey);
const result = fetchData(...args);
cache.set(cacheKey, result);
return result;
}
// Awaited: unwrap Promise return types
async function getUser(id: string): Promise<User> { ... }
type UserResult = Awaited<ReturnType<typeof getUser>>;
// User (not Promise<User>)Building Custom Utility Types
Once you understand the built-in utilities, you can build your own using mapped types, conditional types, and template literal types. Here are practical custom utilities used in production codebases.
// DeepPartial: recursively make all properties optional
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
interface Config {
server: { host: string; port: number };
database: { url: string; pool: { min: number; max: number } };
}
// All nested properties are optional
type PartialConfig = DeepPartial<Config>;
// Nullable: make a type nullable
type Nullable<T> = T | null;
// PickByType: pick properties that match a specific value type
type PickByType<T, ValueType> = {
[K in keyof T as T[K] extends ValueType ? K : never]: T[K];
};
type StringFields = PickByType<User, string>;
// { id: string; name: string; email: string }
// RequireAtLeastOne: at least one property must be provided
type RequireAtLeastOne<T> = {
[K in keyof T]-?: Required<Pick<T, K>> & Partial<Omit<T, K>>;
}[keyof T];
type SearchParams = RequireAtLeastOne<{
name: string;
email: string;
id: string;
}>;
// Must provide at least name, email, or id
// StrictOmit: Omit that errors on non-existent keys
type StrictOmit<T, K extends keyof T> = Omit<T, K>;
// StrictOmit<User, 'nonExistent'> -> compile error!
// Omit<User, 'nonExistent'> -> silently returns UserBest Practices
- Derive, do not duplicate -- use utility types to create variants from a single source type instead of maintaining parallel interfaces that drift apart.
- Name derived types clearly --
CreateUserInputis better thanUserPartialOmitId. The name should describe usage, not implementation. - Use Pick for API boundaries -- define exactly which fields an API endpoint accepts or returns. This documents the contract and catches breaking changes.
- Combine utilities --
Partial<Omit<User, 'id'>>is perfectly valid and reads clearly: "optional fields except id." - Prefer Record over index signatures --
Record<string, User>is more explicit than{ [key: string]: User }and works better with constrained key types. - Do not over-abstract -- complex nested utility types become unreadable. If a type is hard to understand, define it explicitly with an interface.
Frequently Asked Questions
What is the difference between Pick and Omit in TypeScript?
Pick<T, K> creates a new type by selecting specific properties from T. Omit<T, K> creates a new type by excluding specific properties from T. They are complementary: Pick is best when you need a few properties from a large type, while Omit is best when you need most properties but want to remove a few. For example, Pick<User, "id" | "name"> keeps only id and name, while Omit<User, "password"> keeps everything except password.When should I use Partial vs Required in TypeScript?
Partial<T> when you need a type where all properties are optional, such as update functions that accept partial data. Use Required<T> when you need to ensure all properties are provided, such as config objects after merging with defaults. Partial makes every property optional (?), while Required removes the optional modifier from every property.How do I create custom utility types in TypeScript?
type Readonly<T> = { readonly [P in keyof T]: T[P] } makes all properties read-only. Conditional types use the syntax T extends U ? X : Y to create types that depend on conditions. Combine these with infer, keyof, and generic constraints to build powerful reusable type transformations.Format Your TypeScript Data
Working with JSON APIs, configs, or test fixtures in TypeScript? Format, validate, and inspect your JSON data with our free developer tools.