TypeScript Utility Types
A practical reference for TypeScript's built-in utility types including Partial, Pick, Omit, Record, ReturnType, and how to compose custom utilities.
TL;DR
- 01Utility types are generic helpers built into TypeScript that transform existing types into new ones without duplicating code.
- 02The most-used utilities — Partial, Required, Readonly, Pick, Omit, and Record — cover the vast majority of everyday type-transformation needs.
- 03You can compose multiple utility types together or build your own using mapped types and conditional types for any pattern the built-ins do not handle.
Tips
- 01Hover over any utility type in VS Code and TypeScript will expand the resolved type in the tooltip — a fast way to verify the transformation behaves exactly as you expect without running the compiler.
- 02Use <code>Omit</code> rather than <code>Pick</code> when extending a third-party interface — you avoid breakage when upstream adds new fields you would want to keep automatically without touching your type definition.
- 03<code>Awaited<T></code> (TypeScript 4.5+) recursively unwraps nested <code>Promise</code> wrappers. Combine it with <code>ReturnType</code> as <code>Awaited<ReturnType<typeof myFn>></code> to get the resolved value type of any async function in a single expression.
Warnings
- 01<code>Readonly<T></code> does not freeze the object at runtime — it only prevents compile-time reassignment. Use <code>Object.freeze()</code> for runtime immutability, or write a recursive <code>DeepReadonly</code> utility if nested object mutation must also be caught by the compiler.
- 02Deeply recursive utility types can slow TypeScript's language server noticeably on large union or object types. If autocomplete lags, add a depth-counter type parameter to short-circuit recursion after 5–10 levels.
What Utility Types Are
Utility types are generic type aliases shipped with TypeScript's standard library that accept one or more type arguments and return a transformed version of that type. They prevent you from hand-writing repetitive mapped types and keep your codebase consistent across teams.
All utility types are purely compile-time constructs — they emit zero JavaScript. They work by combining three lower-level features: mapped types ({ [K in keyof T]: ... }), conditional types (T extends U ? X : Y), and the infer keyword for extracting nested type information at the structural level.
| Utility Type | Input | What It Returns | Since TS |
|---|---|---|---|
Partial<T> | Object type | All properties made optional | 2.1 |
Required<T> | Object type | All optional props made required | 2.8 |
Readonly<T> | Object type | All properties read-only | 2.1 |
Pick<T, K> | Object type + key union | Subset of T with only keys K | 2.1 |
Omit<T, K> | Object type + key union | T minus keys K | 3.5 |
Record<K, V> | Key union + value type | Object with keys K and values V | 2.1 |
ReturnType<T> | Function type | Function's return type | 2.8 |
Awaited<T> | Promise or plain type | Recursively unwrapped value type | 4.5 |
Partial, Required, and Readonly
Partial<T> makes every property in T optional by adding the ? modifier. It is most useful when writing update or PATCH functions where only a subset of fields may be present. It is shallow — nested object types are not recursively made optional.
Required<T> strips the ? modifier from every property, enforcing a fully-populated model. Use it after a factory function fills in defaults or after a validation step guarantees all fields are present.
Readonly<T> adds the readonly modifier to every property, causing the compiler to reject any mutations. Like the others, it is shallow by default.
| Utility | Modifier Applied | Common Use Case | Deep? |
|---|---|---|---|
Partial<T> | Adds ? to all props | PATCH request body, form draft state | No |
Required<T> | Removes ? from all props | Post-validation model, factory output | No |
Readonly<T> | Adds readonly to all props | Immutable config objects, Redux state | No |
- Use
Partial<Pick<T, K>>to make only a specific subset of keys optional — a common pattern for form field groups. - The
-?modifier in a custom mapped type removes optionality on a per-property basis, giving finer control thanRequired.
Pick, Omit, and Extract
Pick<T, K> constructs a new type containing only the keys listed in the union K. It is the right tool when a function or component needs an explicit subset of a larger model, making the dependency surface clear and narrow.
Omit<T, K> does the opposite — it removes listed keys and returns everything else. Prefer Omit when the excluded set is small but the kept set is large, especially when extending third-party interfaces whose future fields you want to keep automatically.
Extract<T, U> and its counterpart Exclude<T, U> operate on union types, not object types. Extract keeps union members assignable to U; Exclude removes them. NonNullable<T> is shorthand for Exclude<T, null | undefined>.
| Utility | Operates On | Example Input | Result |
|---|---|---|---|
Pick<T, K> | Object type | Pick<User, 'id'|'name'> | { id: number; name: string } |
Omit<T, K> | Object type | Omit<User, 'password'> | User without the password field |
Extract<T, U> | Union type | Extract<'a'|'b'|'c', 'a'|'c'> | 'a' | 'c' |
Exclude<T, U> | Union type | Exclude<'a'|'b'|'c', 'a'> | 'b' | 'c' |
NonNullable<T> | Union type | NonNullable<string|null> | string |
Record, ReturnType, and Parameters
Record<K, V> builds an object type whose keys are members of the union K and whose values are all of type V. It is ideal for lookup tables, enum-keyed maps, and exhaustive switch alternatives where every key must be explicitly handled.
ReturnType<T> extracts the return type of a function type. This is invaluable when you depend on a function's output shape but cannot import its return type directly — for example, with third-party library factory functions that do not export their types.
Parameters<T> extracts parameter types as a tuple. ConstructorParameters<T> does the same for class constructors. InstanceType<T> extracts the class instance type from a constructor. Pair these with spread syntax to forward arguments type-safely through wrappers.
| Utility | Input Example | Resolves To |
|---|---|---|
Record<'USD'|'EUR', number> | Key union + value type | { USD: number; EUR: number } |
ReturnType<typeof fetch> | Function type | Promise<Response> |
Awaited<ReturnType<typeof fetch>> | Async function type | Response |
Parameters<typeof parseInt> | Function type | [string, (number | undefined)?] |
InstanceType<typeof Date> | Constructor type | Date |
Building Custom Utility Types
When the built-ins do not cover a pattern, build your own using mapped types, conditional types, and the infer keyword. Custom utilities follow the same generic signature as the built-ins and compose identically — they can be used as arguments to other utility types.
Common custom utilities include DeepPartial<T> (recursively optional), Mutable<T> (removes all readonly modifiers, useful in tests), and UnwrapPromise<T> (the pre-4.5 equivalent of Awaited). Keep these in a shared src/types/utils.ts file.
| Custom Utility | Definition Pattern | Use Case |
|---|---|---|
DeepPartial<T> | { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] } | Deeply nested patch payloads |
Mutable<T> | { -readonly [K in keyof T]: T[K] } | Strip readonly in test setup |
PickByValue<T, V> | Mapped + conditional type filtering by value type | Keep only string-valued props |
UnwrapPromise<T> | T extends Promise<infer R> ? R : T | Pre-TS 4.5 Awaited equivalent |
Nullable<T> | { [K in keyof T]: T[K] | null } | Database row types that allow NULL |
- Use the
-readonlyand-?modifier prefixes in mapped types to remove existing modifiers rather than add them. - Add type-level unit tests using the
tsdorexpect-typepackage to catch regressions when TypeScript is upgraded.
FAQ
Pick creates a new type by selecting only the specified keys from a type, while Omit creates a new type by excluding the specified keys. Use Pick when you want a small subset of a large type, and Omit when you want most fields minus a few exceptions.
Yes, utility types are composable — for example, Partial<Pick<User, 'name' | 'email'>> creates a type with only those two fields made optional. You can chain as many transformations as needed to express complex type shapes concisely.
Use Record<K, V> when the set of keys is a known union type, such as Record<'admin' | 'user', Permission>, because it enforces that every key in the union is present. A plain index signature ({ [key: string]: V }) is more appropriate when keys are arbitrary and not enumerable at compile time.
ReturnType<T> extracts the return type of a function type, and Parameters<T> extracts its argument types as a tuple. Both are useful for deriving types from existing functions without duplicating type declarations, especially when working with third-party functions whose source types are not exported.
Custom utility types are written as generic types using mapped types (e.g., { [K in keyof T]: ... }) and conditional types (e.g., T extends U ? X : Y). For example, type Mutable<T> = { -readonly [K in keyof T]: T[K] } removes the readonly modifier from every property of T.