TypeScript Utility Types You Should Know
TypeScript includes built-in utility types that transform existing types into new ones. They save you from writing repetitive type definitions and make your code more expressive.
Partial and Required
Partial<T> makes all properties optional. Required<T> makes all properties required:
interface User {
id: number;
name: string;
email: string;
}
// All fields optional — useful for update operations
type UpdateUser = Partial<User>;
// { id?: number; name?: string; email?: string }
function updateUser(id: number, updates: UpdateUser): void {
// Only send provided fields
}
Pick and Omit
Pick<T, K> selects specific properties. Omit<T, K> removes specific properties:
// Only expose safe fields
type UserPublic = Pick<User, 'id' | 'name'>;
// { id: number; name: string }
// Remove sensitive fields
type UserSafe = Omit<User, 'email'>;
// { id: number; name: string }
Record
Record<K, V> creates an object type with specific keys and a uniform value type:
type StatusCode = 200 | 404 | 500;
const statusMessages: Record<StatusCode, string> = {
200: 'OK',
404: 'Not Found',
500: 'Internal Server Error',
};
// String-keyed records
const cache: Record<string, { value: unknown; expires: number }> = {};
Readonly
Readonly<T> makes all properties immutable:
const config: Readonly<DatabaseConfig> = {
host: 'localhost',
port: 5432,
};
// config.port = 3306; // Error: Cannot assign to 'port' because it is read-only
ReturnType and Parameters
ReturnType<T> extracts the return type of a function. Parameters<T> extracts parameter types as a tuple:
function createUser(name: string, age: number): User {
return { id: 1, name, email: '' };
}
type UserResult = ReturnType<typeof createUser>;
// User
type CreateUserParams = Parameters<typeof createUser>;
// [string, number]
Extract and Exclude
Extract<T, U> keeps types that extend U. Exclude<T, U> removes types that extend U:
type Response = string | number | boolean | null;
type NonNull = Exclude<Response, null>;
// string | number | boolean
type OnlyString = Extract<Response, string>;
// string
Awaited
Awaited<T> unwraps a Promise type — useful for async function return types:
async function fetchData(): Promise<{ id: number; name: string }> {
const res = await fetch('/api/data');
return res.json();
}
type DataResult = Awaited<ReturnType<typeof fetchData>>;
// { id: number; name: string }
Combining Utilities
Real power comes from combining these:
interface Todo {
id: number;
title: string;
completed: boolean;
createdAt: Date;
userId: number;
}
// Create DTO: omit auto-generated fields, make everything optional
type CreateTodo = Omit<Todo, 'id' | 'createdAt'>;
// { title: string; completed: boolean; userId: number }
// Update DTO: everything optional
type UpdateTodo = Partial<CreateTodo>;
// { title?: string; completed?: boolean; userId?: number }
// API response: readonly for consumers
type TodoResponse = Readonly<Todo>;
These utilities eliminate boilerplate and keep your types DRY. Instead of maintaining parallel type definitions that drift apart over time, derive them from the source of truth.