DeepPartial and DeepReadonly
Build recursive versions of Partial and Readonly.
DeepPartial and DeepReadonly is a free TypeScript Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the TypeScript Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why Deep Utility Types?
Built-in Partial and Readonly only apply one level deep. For nested objects, you need recursive versions.
interface Config {
db: { host: string; port: number };
app: { name: string };
}
type Shallow = Partial<Config>;
// Shallow.db is still required — only top level is optionalImplementing DeepPartial
Recursively map over object properties, making each one optional and recursively applying DeepPartial if the property value is an object.
type DeepPartial<T> = T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: T;DeepPartial in Action
Apply DeepPartial to a nested config type and see all levels become optional.
type PartialConfig = DeepPartial<Config>;
// PartialConfig.db?: { host?: string; port?: number }
const c: PartialConfig = { db: { host: "localhost" } };
// No error — all other fields are optionalImplementing DeepReadonly
Make every property at every depth readonly by recursing into nested object types.
type DeepReadonly<T> = T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T;DeepReadonly in Action
A deeply frozen config cannot be mutated at any level.
type ReadonlyConfig = DeepReadonly<Config>;
const cfg: ReadonlyConfig = { db: { host: "localhost", port: 5432 }, app: { name: "my-app" } };
// cfg.db.port = 3000; // Error: readonlyHandling Arrays in Deep Types
Arrays need special handling — map element types recursively rather than treating arrays as plain objects.
type DeepPartial<T> =
T extends (infer U)[]
? DeepPartial<U>[]
: T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: T;Handling Primitives
The base case of the recursion must return primitive types as-is, not wrapped in an object mapped type.
type DeepReadonly<T> =
T extends string | number | boolean | null | undefined ? T
: T extends (infer U)[] ? ReadonlyArray<DeepReadonly<U>>
: { readonly [K in keyof T]: DeepReadonly<T[K]> };Combining DeepPartial and DeepReadonly
You can combine them to create a deeply partial and readonly type for safe configuration patching.
type SafePatch<T> = DeepReadonly<DeepPartial<T>>;Performance Considerations
Recursive types on very large objects can slow TypeScript type checking. Consider depth limits or lazy evaluation for performance-critical code.
// For very deep schemas, consider using Zod or io-ts
// which provide runtime + compile-time deep typingReal-World Use: React State Patches
DeepPartial is perfect for state patch functions that accept only the changed subset of a large state object.
function patch<T>(state: T, changes: DeepPartial<T>): T {
return { ...state, ...changes } as T;
}Recap: Deep Utility Types
DeepPartial and DeepReadonly are recursive mapped types that apply their modifier to every level of a nested object. Handle arrays separately and use base cases for primitives.
Quick Check
What is the correct recursive base case for DeepPartial?
What You Learned
You implemented DeepPartial and DeepReadonly using recursive mapped types. Handle arrays as a special case and always return primitives unchanged in the base case.
Frequently asked questions
Is the “DeepPartial and DeepReadonly” lesson free?
Yes — the full text of “DeepPartial and DeepReadonly” is free to read here on the web, and the TypeScript Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the TypeScript Academy course, upgrade to CoddyKit PRO.
What will I learn in “DeepPartial and DeepReadonly”?
Build recursive versions of Partial and Readonly. You practise TypeScript Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start TypeScript Academy?
No prior experience is required. TypeScript Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “DeepPartial and DeepReadonly” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this TypeScript Academy lesson?
Yes. Every TypeScript Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- DeepPartial and DeepReadonly
- Flatten and UnwrapPromise Utilities
- TupleToUnion and UnionToIntersection
- Publishing a Type Utility Library