Utility Types
Partial, Pick, Omit, Record, ReturnType — the tools you should reach for first.
Utility Types
TypeScript ships about a dozen built-in utility types — small, generic transformations over object types. Knowing them by name saves you from reinventing every transformation. Here are the ones you'll use weekly, in order of frequency.
The big four
Partial<T>
Makes every property optional.
interface User { id: number; name: string; email: string }
type UserDraft = Partial<User>;
// → { id?: number; name?: string; email?: string }
Use cases: PATCH endpoint payloads, form drafts, in-progress object construction. Anywhere "the same shape, but maybe missing fields".
Pick<T, K>
Selects a subset of properties.
type PublicUser = Pick<User, "id" | "name">;
// → { id: number; name: string }
Use cases: API response shapes that hide internal fields, function arguments that need only a slice of an entity.
Omit<T, K>
The dual of Pick — removes a subset.
type SafeUser = Omit<User, "email">;
// → { id: number; name: string }
When the exclusion list is shorter than the inclusion list, Omit reads more naturally:
// Same result, but Omit is clearly easier to read.
Pick<User, "id" | "name"> // 2 keys to include
Omit<User, "email"> // 1 key to exclude
Record<K, V>
Builds an object whose keys are K and values are V.
type Color = "red" | "green" | "blue";
type Palette = Record<Color, string>;
// → { red: string; green: string; blue: string }
type Counts = Record<string, number>;
// → { [k: string]: number } ← string-keyed dictionary
Use cases: enumerated maps (every member of a union has a value), string-keyed dictionaries, lookup tables.
The function-type reflectors
These pull types out of existing function and promise types:
ReturnType<F>
function loadUser(id: number) { return { id, name: "x" }; }
type Loaded = ReturnType<typeof loadUser>;
// → { id: number; name: string }
The typeof loadUser is the function's type (not a value). ReturnType reads the return slot of that function type. Massively useful when the function's return type is anonymous (object literal).
Parameters<F>
type LoadParams = Parameters<typeof loadUser>;
// → [id: number]
Returns a tuple of the parameter types — order preserved.
Awaited<T>
Unwraps Promise<X> recursively (handles Promise<Promise<X>> too):
type R = Awaited<Promise<{ ok: boolean }>>;
// → { ok: boolean }
Often combined with ReturnType for async functions:
async function fetchUser() { /* ... */ return { id: 1 } }
type FetchedUser = Awaited<ReturnType<typeof fetchUser>>;
// → { id: number }
The modifier toggles
Required<T>
Inverse of Partial — makes every property required:
type Filled = Required<UserDraft>; // { id: number; name: string; email: string }
Readonly<T>
Marks every property readonly:
type Frozen = Readonly<User>;
// frozen.id = 5 ❌ Cannot assign to 'id' because it is a read-only property
NonNullable<T>
Strips null and undefined:
type S = NonNullable<string | null | undefined>; // string
The class reflectors
Less commonly used, but real:
class Service { constructor(public name: string, public port: number) {} }
type SvcInstance = InstanceType<typeof Service>; // Service
type SvcCtorArgs = ConstructorParameters<typeof Service>; // [name: string, port: number]
If you've ever written a generic factory that takes a class and produces an instance, you've used these.
Composition is fine
Utility types are just generic types — compose them freely:
type ApiResponse = Readonly<Required<Pick<User, "id" | "name">>>;
// → readonly id: number; readonly name: string
type MaybeUser = Partial<NonNullable<User>>;
There's no "depth limit" or perf cliff for sensible nesting. If a composition is hard to read, alias the inner pieces:
type PublicUser = Pick<User, "id" | "name">;
type ApiUser = Readonly<PublicUser>;
What they look like underneath
Each utility is one of three kinds of type:
- Mapped types (
Partial,Required,Readonly,Record,Pick):type Partial<T> = { [K in keyof T]?: T[K] }; type Required<T> = { [K in keyof T]-?: T[K] }; type Readonly<T> = { readonly [K in keyof T]: T[K] }; type Record<K extends PropertyKey, V> = { [P in K]: V }; type Pick<T, K extends keyof T> = { [P in K]: T[P] }; - Conditional types with
infer(ReturnType,Parameters,Awaited,InstanceType):type ReturnType<F> = F extends (...args: any) => infer R ? R : never; type Parameters<F> = F extends (...args: infer P) => any ? P : never; - Union exclusion (
Exclude,Extract,Omit,NonNullable):type Exclude<T, U> = T extends U ? never : T; type Extract<T, U> = T extends U ? T : never; type Omit<T, K> = Pick<T, Exclude<keyof T, K>>; type NonNullable<T> = T & {}; // (effectively)
You don't need to memorise these definitions, but knowing they're just regular generics demystifies them — when you need a custom one, you write one with the same machinery.
When to write your own
The built-ins solve common cases. When they don't fit — say you need "Pick where each value becomes optional" — you compose:
type PartialPick<T, K extends keyof T> = Partial<Pick<T, K>>;
Or you write one from scratch:
// Only the keys whose values are functions
type FunctionKeys<T> = {
[K in keyof T]: T[K] extends (...args: any) => any ? K : never;
}[keyof T];
The bigger your utility-type library, the more your application code looks like data wrangling — exactly the level of abstraction TypeScript is best at.
Tools in the wild
3 tools- librarytype-festfree tier
120+ utility types — RequireAtLeastOne, Merge, Promisable, SetOptional, etc.
- libraryts-essentialsfree tier
DeepPartial, DeepReadonly, DeepRequired and other recursive variants.
- serviceTypeScript Playgroundfree tier
Use `// $ExpectType` and hover to verify the resolved structure of compositions.