TypeScript Utility Types Cheat Sheet
Every built-in utility type with practical examples -- Partial, Pick, Omit, Record, ReturnType, Awaited, and string manipulation types.
Partial, Required, Readonly
Modify property modifiers across an entire type in one step.
interface User {
id: number;
name: string;
email: string;
}
type UserUpdate = Partial<User>;
// => { id?: number; name?: string; email?: string }
function updateUser(id: number, changes: Partial<User>): void {
// changes.name is string | undefined
}
updateUser(1, { name: 'Bob' }); // => OK, email not requiredinterface Config {
host?: string;
port?: number;
debug?: boolean;
}
type ResolvedConfig = Required<Config>;
// => { host: string; port: number; debug: boolean }
function startServer(config: Required<Config>): void {
console.log(config.host); // => type: string (not string | undefined)
}interface Settings {
theme: string;
fontSize: number;
}
const defaults: Readonly<Settings> = { theme: 'dark', fontSize: 14 };
// defaults.theme = 'light'; // => Error: cannot assign to readonly property
// Readonly is shallow -- nested objects are still mutable
interface Deep { nested: { value: number } }
const obj: Readonly<Deep> = { nested: { value: 1 } };
obj.nested.value = 2; // => OK (Readonly doesn't recurse)For deep readonly, use a library like ts-essentials DeepReadonly or a recursive mapped type.
Pick and Omit
Select or exclude properties from an existing type to create a narrower type.
interface User {
id: number;
name: string;
email: string;
password: string;
}
type UserPreview = Pick<User, 'id' | 'name'>;
// => { id: number; name: string }
type LoginCredentials = Pick<User, 'email' | 'password'>;
// => { email: string; password: string }type PublicUser = Omit<User, 'password'>;
// => { id: number; name: string; email: string }
type CreateUserInput = Omit<User, 'id'>;
// => { name: string; email: string; password: string }Omit doesn't error on invalid keys: `Omit<User, 'nonexistent'>` silently returns the full type. Use a custom StrictOmit if you need compile-time safety.
// Optional updates excluding id
type UserUpdate = Partial<Omit<User, 'id'>>;
// => { name?: string; email?: string; password?: string }
// Required subset
type RequiredProfile = Required<Pick<User, 'name' | 'email'>>;
// => { name: string; email: string }Record
Create object types with specific key and value types.
type StatusColors = Record<string, string>;
const colors: StatusColors = {
success: '#22c55e',
warning: '#eab308',
error: '#ef4444',
};type Role = 'admin' | 'editor' | 'viewer';
const permissions: Record<Role, string[]> = {
admin: ['read', 'write', 'delete'],
editor: ['read', 'write'],
viewer: ['read'],
};
// Missing a role key => compile errorRecord<UnionKey, V> enforces that every union member has an entry. This is the easiest way to guarantee exhaustive mappings.
// These are equivalent for string keys:
type A = Record<string, number>;
type B = { [key: string]: number };
// Record is more flexible -- works with union keys:
type StatusMap = Record<'ok' | 'error', string>;
// => { ok: string; error: string }Extract and Exclude
Filter union type members by assignability.
type Input = string | number | boolean | null;
type StringOrNumber = Extract<Input, string | number>;
// => string | number
type Falsy = Extract<Input, false | null | 0 | ''>;
// => null (only null is in both)type Input = string | number | boolean | null;
type NonNull = Exclude<Input, null>;
// => string | number | boolean
type NotString = Exclude<Input, string>;
// => number | boolean | nulltype Event =
| { type: 'click'; x: number; y: number }
| { type: 'keypress'; key: string }
| { type: 'scroll'; offset: number };
type ClickEvent = Extract<Event, { type: 'click' }>;
// => { type: 'click'; x: number; y: number }
type NonScrollEvent = Exclude<Event, { type: 'scroll' }>;
// => { type: 'click'; x: number; y: number } | { type: 'keypress'; key: string }NonNullable
Strip `null` and `undefined` from a type.
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>;
// => stringinterface FormData {
name: string | null;
email: string | undefined;
age: number | null | undefined;
}
type RequiredFormData = {
[K in keyof FormData]: NonNullable<FormData[K]>;
};
// => { name: string; email: string; age: number }NonNullable is shorthand for Exclude<T, null | undefined>.
ReturnType and Parameters
Extract the return type or parameter types from function types.
function fetchUser(id: number) {
return { id, name: 'Alice', active: true };
}
type User = ReturnType<typeof fetchUser>;
// => { id: number; name: string; active: boolean }Use `typeof` to get the function type from a value. ReturnType works on types, not values.
function createUser(name: string, age: number, role: 'admin' | 'user') {
// ...
}
type CreateArgs = Parameters<typeof createUser>;
// => [name: string, age: number, role: 'admin' | 'user']
type FirstArg = CreateArgs[0]; // => string
type ThirdArg = CreateArgs[2]; // => 'admin' | 'user'class Database {
constructor(host: string, port: number) {}
}
type DbArgs = ConstructorParameters<typeof Database>;
// => [host: string, port: number]Awaited
Unwrap the resolved type of a Promise (recursively).
type A = Awaited<Promise<string>>; // => string
type B = Awaited<Promise<Promise<number>>>; // => number (recursive)
type C = Awaited<string | Promise<number>>; // => string | numberasync function loadConfig() {
const response = await fetch('/api/config');
return response.json() as Promise<{ apiUrl: string; debug: boolean }>;
}
type Config = Awaited<ReturnType<typeof loadConfig>>;
// => { apiUrl: string; debug: boolean }Awaited<ReturnType<typeof fn>> is the standard pattern for getting the resolved return type of an async function.
String Manipulation Types
Built-in types that transform string literal types at the type level.
type A = Uppercase<'hello'>; // => 'HELLO'
type B = Lowercase<'HELLO'>; // => 'hello'
type C = Capitalize<'hello'>; // => 'Hello'
type D = Uncapitalize<'Hello'>; // => 'hello'type EventName = 'click' | 'scroll' | 'keypress';
type UpperEvents = Uppercase<EventName>;
// => 'CLICK' | 'SCROLL' | 'KEYPRESS'
type HandlerName = `on${Capitalize<EventName>}`;
// => 'onClick' | 'onScroll' | 'onKeypress'String manipulation types distribute over unions automatically.
Composing Utility Types
Combine utility types to build precisely the shape you need.
interface Article {
id: string;
title: string;
body: string;
authorId: string;
createdAt: Date;
updatedAt: Date;
}
// POST request: no id or timestamps
type CreateArticle = Omit<Article, 'id' | 'createdAt' | 'updatedAt'>;
// PATCH request: partial updates, no changing id
type UpdateArticle = Partial<Omit<Article, 'id' | 'createdAt' | 'updatedAt'>>;
// GET response: everything, readonly
type ArticleResponse = Readonly<Article>;type WithRequired<T, K extends keyof T> = T & Required<Pick<T, K>>;
interface Options {
timeout?: number;
retries?: number;
baseUrl?: string;
}
type ResolvedOptions = WithRequired<Options, 'baseUrl'>;
// => { timeout?: number; retries?: number; baseUrl: string }This pattern intersects the original type with a Required subset -- a common custom utility.
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
interface Config {
database: { host: string; port: number };
cache: { ttl: number; maxSize: number };
}
type PartialConfig = DeepPartial<Config>;
// database?.host, database?.port, cache?.ttl, cache?.maxSize all optionalCan you write this from memory?
Create type Scores for an object with string keys and number values