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

  1. 01Utility types are generic helpers built into TypeScript that transform existing types into new ones without duplicating code.
  2. 02The most-used utilities — Partial, Required, Readonly, Pick, Omit, and Record — cover the vast majority of everyday type-transformation needs.
  3. 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

  1. 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.
  2. 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.
  3. 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

  1. 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.
  2. 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 TypeInputWhat It ReturnsSince TS
Partial<T>Object typeAll properties made optional2.1
Required<T>Object typeAll optional props made required2.8
Readonly<T>Object typeAll properties read-only2.1
Pick<T, K>Object type + key unionSubset of T with only keys K2.1
Omit<T, K>Object type + key unionT minus keys K3.5
Record<K, V>Key union + value typeObject with keys K and values V2.1
ReturnType<T>Function typeFunction's return type2.8
Awaited<T>Promise or plain typeRecursively unwrapped value type4.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.

UtilityModifier AppliedCommon Use CaseDeep?
Partial<T>Adds ? to all propsPATCH request body, form draft stateNo
Required<T>Removes ? from all propsPost-validation model, factory outputNo
Readonly<T>Adds readonly to all propsImmutable config objects, Redux stateNo
  • 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 than Required.

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>.

UtilityOperates OnExample InputResult
Pick<T, K>Object typePick<User, 'id'|'name'>{ id: number; name: string }
Omit<T, K>Object typeOmit<User, 'password'>User without the password field
Extract<T, U>Union typeExtract<'a'|'b'|'c', 'a'|'c'>'a' | 'c'
Exclude<T, U>Union typeExclude<'a'|'b'|'c', 'a'>'b' | 'c'
NonNullable<T>Union typeNonNullable<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.

UtilityInput ExampleResolves To
Record<'USD'|'EUR', number>Key union + value type{ USD: number; EUR: number }
ReturnType<typeof fetch>Function typePromise<Response>
Awaited<ReturnType<typeof fetch>>Async function typeResponse
Parameters<typeof parseInt>Function type[string, (number | undefined)?]
InstanceType<typeof Date>Constructor typeDate

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 UtilityDefinition PatternUse 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 typeKeep only string-valued props
UnwrapPromise<T>T extends Promise<infer R> ? R : TPre-TS 4.5 Awaited equivalent
Nullable<T>{ [K in keyof T]: T[K] | null }Database row types that allow NULL
  • Use the -readonly and -? modifier prefixes in mapped types to remove existing modifiers rather than add them.
  • Add type-level unit tests using the tsd or expect-type package to catch regressions when TypeScript is upgraded.

FAQ