Siddhant DevalAuthor
Senior Full-Stack Engineer·Aug 27, 2026·13 min read
Utility Types & Mapped Types: The Standard Library
The built-in utility types are not magic — they are one-liners built with mapped types. Reading their source and building your own reveals the full power of type-level transformation and eliminates the need to reach for `any` in complex object manipulation.
Technical Series
TypeScript Mastery
Part 4 of 11
Utility Types & Mapped Types: The Standard Library
Types are a specification language — not an annotation layer. The built-in utility types are the most visible part of that language's standard library. But most developers treat them as magic incantations —
Partial<T> makes everything optional, Readonly<T> makes everything readonly, and that's that. Read the source code. Partial<T> is three tokens: { [K in keyof T]?: T[K] }. Understanding the mechanism means you can compose new utilities, not just consume existing ones.1. Mapped Types — The Mechanism
A mapped type iterates over the keys of a type and transforms each property. The syntax is deliberately similar to a
for...in loop:typescript
Kis a type variable bound to each key ofTin turnkeyof Tproduces the union of all keys ofTas literal string types- The right side can reference
T[K](the type of the current property)
1.1 Reading the Source of Partial<T>
typescript
1.2 The Full Utility Type Taxonomy
| Type | Source Implementation | What It Does |
|---|---|---|
Partial<T> | { [K in keyof T]?: T[K] } | Makes all properties optional |
Required<T> | { [K in keyof T]-?: T[K] } | Removes optionality from all properties |
Readonly<T> | { readonly [K in keyof T]: T[K] } | Makes all properties readonly |
Pick<T, K> | { [P in K]: T[P] } | Keeps only the listed keys |
Omit<T, K> | Pick<T, Exclude<keyof T, K>> | Removes the listed keys |
Record<K, V> | { [P in K]: V } | Creates an object type with keys K and values V |
Exclude<T, U> | T extends U ? never : T | Removes U from union T |
Extract<T, U> | T extends U ? T : never | Keeps only U from union T |
NonNullable<T> | T extends null | undefined ? never : T | Removes null and undefined |
ReturnType<T> | T extends (...args: any) => infer R ? R : any | Extracts the return type of a function |
Parameters<T> | T extends (...args: infer P) => any ? P : never | Extracts the parameter types as a tuple |
Awaited<T> | Recursive conditional (unwraps Promise chains) | Unwraps Promise<T> recursively |
2. Modifiers — +?, -?, +readonly, -readonly
Mapped types support modifier tokens to add or remove optionality and readonly-ness:
typescript
Crucial Requirement
The
-? modifier is what makes Required<T> work. Without it, a plain mapped type preserves the ? modifier from the source — { [K in keyof T]: T[K] } produces a type with the same optionality as T. The -? strips it.
Expand
3. Key Remapping with as
You can rename or filter keys in a mapped type using the
as clause:typescript

Expand
4. Homomorphic vs Non-Homomorphic Mapped Types
This distinction determines whether a mapped type preserves modifiers from the source type:
typescript
Architectural Note
All of TypeScript's built-in utility types that iterate
keyof T are homomorphic. Record<K, V> is non-homomorphic because it takes the keys as a fresh parameter, not from a source type's keyof.5. Building Deep Utilities
Flat mapped types only transform the top level. To recurse into nested objects, combine mapped types with conditional types:
typescript
![Tree diagram showing DeepReadonly<Config> recursion. Root node: Config. Two child nodes: server and auth. Each has a dashed DeepReadonly<> call wrapping it. Under server: leaf nodes host: string and port: number, both with a readonly tag. Under auth: leaf nodes secret: string and expiresIn: number, both with a readonly tag. A decision diamond at each branch node reads: 'Is T[K] an object (non-function)? → recurse : apply readonly to leaf'. Caption: 'DeepReadonly<T> combines a mapped type with a recursive conditional — the conditional selects between recursion and the base case at each property'.](/assets/blog/frontend/typescript-utility-mapped-types/fig-03.png)
Expand
Summary
| Concept | Rule |
|---|---|
| Mapped type | { [K in keyof T]: Transform } — iterates keys and transforms each property |
+? / -? | Add or remove optionality; -? is what makes Required<T> work |
+readonly / -readonly | Add or remove the readonly modifier independently of optionality |
Key remapping (as) | Rename keys with a template literal or filter them by mapping to never |
| Homomorphic | Iterates keyof T — preserves source modifiers; all built-in utilities are homomorphic |
| Non-homomorphic | Iterates a fresh key union — does not preserve source modifiers |
DeepReadonly<T> | Requires a recursive conditional type; flat mapped types only transform one level |
What's Next
In Part 5, we cover Conditional Types &infer— the type-level equivalent of if-else and pattern matching. They are what powerReturnType<T>,Parameters<T>,Awaited<T>, and every type-level algorithm in major TypeScript libraries.
Research & Synthesis Note
This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.
#TypeScript#Utility Types#Mapped Types#Type Transformation#Advanced TypeScript