12 — Utility Types: Built-in Type Transformers
"A grab-bag of helpers to memorize" was my utility-types model, and the list refused to stick. Writing it down reframed them: utility types are built-in generics that transform one type into a related variation — making its properties optional, readonly, picking a subset, omitting some, mapping it to a record. [1] They're functions that operate on types instead of values, and once I saw them that way, the list became a small toolkit rather than a vocabulary test.
The motivation is that real code constantly needs variations of a base type. A form holds a partial draft of a User (some fields, not all). A config object is a User with the password field omitted. A lookup table maps user IDs to User records. Each of these is a transformation of User, and spelling out every variation by hand is repetitive and error-prone. Utility types express the transformation directly, so the variation stays in sync with the base type automatically.
Partial<T> and the shape transformers
Partial<T> makes every property of T optional [1]. The classic use is a form or update payload where any subset of fields is valid:
type User = { id: number; name: string; email: string };
type UserUpdate = Partial<User>;
// { id?: number; name?: string; email?: string }Its companions round out the set:
- Required<T> — the inverse; makes every property required.
- Readonly<T> — makes every property readonly, so they can't be reassigned after construction.
- Pick<T, Keys> — selects only the specified properties from T.
- Omit<T, Keys> — removes the specified properties from T [2].
Pick and Omit are complements — Pick keeps a named subset, Omit drops a named subset, and either can express the other. I reach for Omit<User, "id"> when creating a new user (no id yet), and Pick<User, "id" | "name"> when I only need a slim view.
Record<Keys, Type>
Record<K, V> builds an object type whose keys are K and whose values are V [3]:
type UserMap = Record<string, User>;
// an object with string keys and User valuesThis is the clean way to express "a dictionary/lookup." The keys can be a union of specific strings (Record<"a" | "b", number>), which combines beautifully with keyof to build type-safe maps indexed by an entity's valid keys.
Exclude, Extract, and NonNullable: union operators
These operate on union types rather than object shapes:
- Exclude<Union, Excluded> — removes members from a union [4]. Exclude<"a" | "b" | "c", "a"> is "b" | "c".
- Extract<Union, Extracted> — keeps only the members assignable to a type. Extract<string | number, string> is string.
- NonNullable<T> — removes null and undefined from T. NonNullable<string | null> is string.
These are how I filter and refine unions without rewriting them — Exclude drops unwanted cases, Extract picks matching ones, NonNullable strips absence. They read like set operations because that's exactly what they are.
Function-related utilities
Three utilities operate on function types:
- Parameters<F> — extracts a function's parameter types as a tuple.
- ReturnType<F> — extracts a function's return type.
- InstanceType<C> — extracts the instance type a constructor produces [5].
These matter when I want to derive types from an existing function rather than redeclare them. ReturnType<typeof fetch> gives me the type fetch returns, so if fetch's signature changes, my derived type follows automatically. This "derive, don't redeclare" pattern is what keeps types in sync as a codebase evolves.
Awaited<T>: unwrapping promises
Awaited<T> recursively unwraps a Promise type to the type it resolves to [6]. Awaited<Promise<string>> is string; Awaited<Promise<Promise<number>>> is number (it unwraps nesting). This mirrors what await does at runtime, and it's how async function return types are computed — the compiler uses Awaited internally so that an async function returning Promise<string> is typed as returning Promise<string> to callers, while the resolved value inside is string.
How they fit together
The unifying idea is that every utility is a type-level function — it takes type arguments and returns a transformed type. Reading them as functions (Partial, Pick, Omit) made the list click: each has a clear input and output, and they compose. Omit<Partial<User>, "id"> means "User with all-optional fields and id removed" — a perfectly reasonable update-payload type built from two utilities. The built-ins cover the common transformations; when they don't, the same compositional thinking extends to mapped and conditional types (the next notes).
How I use this
My defaults: reach for Partial for update/patch payloads and form drafts; Pick/Omit for views and creation shapes that drop or select fields; Readonly for values that shouldn't change after construction; Record for dictionaries and lookup tables; Exclude/Extract/NonNullable for refining unions; and Parameters/ReturnType to derive types from existing functions rather than redeclaring them. The discipline that pays off is never spelling out a variation by hand when a utility expresses it — because the hand-written version drifts the moment the base type changes, and the utility version updates itself.
References
[1] Microsoft, "Utility Types," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/utility-types.html
[2] Microsoft, "Omit<Type, Keys>," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys
[3] Microsoft, "Record<Keys, Type>," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type
[4] Microsoft, "Exclude<UnionType, ExcludedMembers>," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers
[5] Microsoft, "InstanceType<Type>," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/utility-types.html#instancetypetype
[6] Microsoft, "Awaited<Type>," TypeScript Handbook, 2024. [Online]. Available: https://www.typescriptlang.org/docs/handbook/utility-types.html#awaitedtype
[7] M. Chenry, "TypeScript Utility Types Guide," 2023. [Online]. Available: https://camchenry.com/blog/typescript-utility-types
Knowledge check · Question 1 of 5
`Partial<User>` produces a type where…
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!