TypeScript's built-in utility types are one of those features where the documentation lists them all with equal weight — implying they're equally important. They're not. Partial, Required, Pick, Omit, and Record show up constantly in real codebases. Intrinsic, ThisParameterType, and OmitThisParameter rarely do. Here's the practical breakdown.
The core four (memorize these)
Partial<T> makes every property in T optional. The most-used utility type by a wide margin: useful for update payloads, form state, and any function that accepts a partial configuration and merges it with defaults.
type User = { id: number; name: string; email: string }
type UpdatePayload = Partial<User>
// { id?: number; name?: string; email?: string }
Required<T> is the inverse — makes every property mandatory. Less common but useful for enforcing completeness after a series of partial merges.
Pick<T, K> builds a new type containing only the specified keys from T. The clean way to define a "view" type or a subset DTO without copy-pasting:
type UserPreview = Pick<User, "id" | "name">
// { id: number; name: string }
Omit<T, K> is the inverse — everything in T except the listed keys. More commonly reached for than Pick in practice, because it's usually easier to say "everything except the password field" than to enumerate all the fields you do want:
type PublicUser = Omit<User, "email">
// { id: number; name: string }
The ones you'll use almost as often
Record<K, V> creates an object type with keys of type K and values of type V. The right answer whenever you want a typed object mapping and { [key: string]: V } feels too loose:
type StatusMessages = Record<"success" | "error" | "loading", string>
Readonly<T> marks all properties as readonly, preventing mutation after construction. Useful for config objects, constants, and anywhere you want to prevent accidental mutation without the runtime overhead of Object.freeze().
ReturnType<T> extracts the return type of a function type. Saves you from duplicating a type definition when a function is already typed and you need to refer to what it returns:
function getUser() { return { id: 1, name: "Alice" } }
type User = ReturnType<typeof getUser>
// { id: number; name: string }
Parameters<T> extracts the parameter tuple of a function type. Useful for HOFs, middleware wrappers, and anywhere you're forwarding arguments through layers without wanting to re-type the signature.
The structural ones: NonNullable, Exclude, Extract
NonNullable<T> removes null and undefined from a union. Common after operations that may return null and you've already checked:
type MaybeString = string | null | undefined
type DefinitelyString = NonNullable<MaybeString> // string
Exclude<T, U> removes from union T all members that are assignable to U. Good for filtering discriminated unions:
type Events = "click" | "focus" | "blur"
type NonFocusEvents = Exclude<Events, "focus" | "blur"> // "click"
Extract<T, U> is the inverse — keeps only the members of T that are assignable to U.
The occasionally useful ones
Awaited<T> unwraps a Promise<T> recursively — the right way to get the resolved type of an async function's return. Replaced the pattern of ReturnType<typeof fn> extends Promise<infer R> ? R : never.
ConstructorParameters<T> and InstanceType<T> work like Parameters and ReturnType but for class constructors. Used when you're building factory functions or dependency injection patterns that need to be typed against class shapes.
ThisType<T> is a marker — it doesn't transform the type, it sets the this context for methods in an object literal. Primarily relevant for options-API-style patterns (Vue 2's component options, mixins) and less so for modern TypeScript.
The ones you'll rarely touch
Uppercase<S>, Lowercase<S>, Capitalize<S>, Uncapitalize<S> are intrinsic string types that transform string literal types at compile time. They're the building blocks for conditional string manipulation in complex mapped types — useful inside library code generating typed string variants, less common in application code.
OmitThisParameter<T> removes the this parameter from a function type. ThisParameterType<T> extracts it. These exist primarily for compatibility with Function.prototype.bind() and are surfaced more in TypeScript's own type definitions than in most user code.
How to build custom utility types
The power of utility types is that they're all built from the same three primitives:
- Mapped types —
{ [K in keyof T]?: T[K] }(that'sPartial, spelled out) - Conditional types —
T extends U ? X : Y infer— for extracting types from within conditional type branches
Understanding Partial as syntactic sugar for a mapped type with ?: means you can extend or modify the pattern: DeepPartial<T> (recursively partial), PickByValue<T, V> (keys whose value matches a type), Flatten<T> — all follow from the same building blocks.
Type-safe boilerplate, faster
If you're building interfaces from JSON shapes or generating enum types from value lists, our JSON to TypeScript Converter and TypeScript Enum Generator handle the structural boilerplate — paste a JSON object or a list of values and get typed interfaces or enums back, ready to use as the T you're passing into utility types.
