0Pricing
TypeScript Academy · Lesson

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 optional

Implementing 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 optional

Implementing 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: readonly

Handling 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 typing

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

  1. DeepPartial and DeepReadonly
  2. Flatten and UnwrapPromise Utilities
  3. TupleToUnion and UnionToIntersection
  4. Publishing a Type Utility Library
← Back to TypeScript Academy