Chapter 16: TypeScript Utility Types
1. First — What are Utility Types really? (the honest explanation)
Utility types are built-in generic type helpers that TypeScript gives you for free. They let you transform, filter, combine, unwrap, or modify existing types without rewriting them manually.
Think of them as type-level functions:
|
0 1 2 3 4 5 6 |
type NewType = Utility<OriginalType, ...parameters>; |
They live in the global scope → no import needed (as long as you’re using TypeScript ≥ 2.1 for the oldest ones).
Official list (as of early 2026 — from typescriptlang.org/docs/handbook/utility-types.html):
- Awaited<T>
- Partial<T>
- Required<T>
- Readonly<T>
- Record<K, T>
- Pick<T, K>
- Omit<T, K>
- Exclude<T, U>
- Extract<T, U>
- NonNullable<T>
- Parameters<T>
- ConstructorParameters<T>
- ReturnType<T>
- InstanceType<T>
- ThisParameterType<T>
- OmitThisParameter<T>
- Uppercase<T>, Lowercase<T>, Capitalize<T>, Uncapitalize<T>
These are the official built-ins. Many teams also create custom ones (we’ll see a few popular patterns at the end).
2. The Most Frequently Used Ones (90% of daily work)
Let’s go through them in order of how often you’ll actually type them.
A. Partial<T> — make everything optional (very very common)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
interface User { id: number; name: string; email: string; isActive: boolean; role: "admin" | "user" | "guest"; } type UserUpdateInput = Partial<User>; // → { id?: number; name?: string; email?: string; ... } function updateUser(id: number, changes: UserUpdateInput) { // safe: you can send only name, or only email+role, etc. } |
Real use-case: → PATCH / PUT requests (REST / tRPC / React Query mutations) → Form default values when editing (only send changed fields)
B. Required<T> — opposite: make everything required
|
0 1 2 3 4 5 6 7 8 |
type CreateUserDto = Required<Pick<User, "name" | "email" | "password">>; // → { name: string; email: string; password: string } |
Use-case: validation shapes (Zod .required(), form libraries)
C. Readonly<T> — freeze the object (immutability)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
type Config = Readonly<{ apiUrl: string; timeout: number; retryCount: number; }>; const appConfig: Config = { apiUrl: "...", timeout: 5000, retryCount: 3 }; // appConfig.timeout = 10000; // Error — readonly! |
Real pattern: app/theme/config objects, Redux state slices (before Immer), props in React
D. Pick<T, K> — select only some keys
|
0 1 2 3 4 5 6 7 8 |
type UserPreview = Pick<User, "id" | "name" | "email">; // → { id: number; name: string; email: string } |
Super common in:
- List views / table rows
- Select / autocomplete responses
- DTOs that expose subset of entity
E. Omit<T, K> — remove some keys (opposite of Pick)
|
0 1 2 3 4 5 6 7 8 |
type UserWithoutPassword = Omit<User, "password">; type SafeUser = Omit<User, "password" | "internalNotes" | "hash">; |
Very frequent when:
- Sending user to frontend
- Logging / auditing
- Creating public-facing types
F. Record<K, T> — object with specific keys & same value type
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
type PermissionsByRole = Record<"admin" | "user" | "guest", boolean>; const perms: PermissionsByRole = { admin: true, user: true, guest: false }; // or very common: type EnvVars = Record<string, string | undefined>; const env: EnvVars = process.env; |
Also: string → component map, error codes → messages, feature flags
G. ReturnType<T> — extract what a function returns
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
async function fetchUser(id: number) { // ... return { id, name: "Sara", email: "sara@example.com" }; } type UserFromApi = ReturnType<typeof fetchUser>; // Promise<{id:number, name:string, email:string}> type UserData = Awaited<ReturnType<typeof fetchUser>>; // {id:number, name:string, email:string} |
Extremely useful with:
- API functions
- Selectors (Redux, Zustand)
- Custom hooks that return complex objects
H. Parameters<T> — extract tuple of function parameters
|
0 1 2 3 4 5 6 7 8 9 10 11 |
function createPost(title: string, content: string, tags?: string[]) { // ... } type CreatePostArgs = Parameters<typeof createPost>; // → [title: string, content: string, tags?: string[] | undefined] |
Use-case: wrappers, middleware, testing argument spies
I. Awaited<T> (since 4.5 — very useful now)
|
0 1 2 3 4 5 6 7 |
type ApiUser = Awaited<ReturnType<typeof fetchUser>>; // unwraps Promise → gets the inner type |
Also handles nested promises: Awaited<Promise<Promise<number>>> → number
3. String utility types (template literal helpers — since 4.1)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
type EventName = "click" | "hover" | "focus"; type OnEvent = `on${Capitalize<EventName>}`; // → "onClick" | "onHover" | "onFocus" type LowerEvent = Lowercase<OnEvent>; // → "onclick" | "onhover" | "onfocus" |
Real usage:
- React event handler names
- CSS class prefixes
- GraphQL field → input names
4. Set-like operations (very powerful for unions)
|
0 1 2 3 4 5 6 7 8 9 10 |
type Animal = "cat" | "dog" | "bird" | "fish"; type Pet = Exclude<Animal, "fish">; // "cat"|"dog"|"bird" type SeaPet = Extract<Animal, "fish" | "crab">; // "fish" type NonNull = NonNullable<string | number | null | undefined>; // string | number |
Use-case:
- Filtering allowed roles / statuses
- Union cleanup after conditional types
5. Quick reference table (print / bookmark this)
| Utility | What it does | Most common real use-case (2026) | Approx. frequency |
|---|---|---|---|
| Partial<T> | All props optional | PATCH requests, form partial updates | ★★★★★ |
| Required<T> | All props required | Creation DTOs, strict validation | ★★★★ |
| Readonly<T> | All props readonly | Configs, props, frozen state | ★★★★ |
| Pick<T, K> | Keep only listed keys | Previews, public shapes, table rows | ★★★★★ |
| Omit<T, K> | Remove listed keys | Remove secrets, internal fields | ★★★★★ |
| Record<K, T> | { [key in K]: T } | Maps, env, feature flags, permissions | ★★★★ |
| ReturnType<T> | Type function returns | API responses, hook returns, selectors | ★★★★ |
| Awaited<T> | Unwrap Promise(s) | async function results | ★★★★ |
| Parameters<T> | Tuple of arguments | Wrappers, test spies | ★★★ |
| Exclude / Extract | Union subtract / intersect | Allowed values filtering | ★★★ |
| NonNullable<T> | Remove null/undefined | After ?? / ! checks | ★★★ |
| Capitalize etc. | String case transformation | Event names, class names | ★★ |
6. Popular custom utility patterns people add (beyond built-ins)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// Very common in many codebases type Mutable<T> = { -readonly [P in keyof T]: T[P] }; type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T; type ValueOf<T> = T[keyof T]; type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; |
Your next steps (mini homework)
- Open TS playground
- Take any interface you have (or copy User from above)
- Try to create:
- UpdateInput = Partial<…>
- PublicUser = Omit<…, “password”>
- UserCreate = Required<Pick<…, “name”|”email”>>
- Config = Readonly<…>
- Response = Awaited<ReturnType<your async fn>>
Which of these feel most useful for your current project right now?
Want to go deeper on:
- DeepPartial / DeepReadonly patterns
- Combining many utilities together (real tRPC / Zod-like shapes)
- Writing your own mapped + conditional utilities
- Utility types in React (props, state, hooks)
Just say — we’ll zoom in there next! 😄
