Skip to content
webtype.org#215 deep-readonly · par 4

No. 215 · July 23, 2026 · Hard

Deep Readonly

Implement `DeepReadonly<T>` so every property, at every depth, becomes `readonly`. Functions must pass through untouched — marking their properties readonly would change nothing and break the equality check.

01

Try the puzzle yourself

Par 4

Puzzle

deep-readonly.ts
Stroke 1 of 4Not run yet

Replace ??? — your solution is checked against the cases below.

Checks

4
  • DeepReadonly<{ a: number }>
    { readonly a: number }
  • DeepReadonly<{ a: { b: string } }>
    { readonly a: { readonly b: string } }
  • DeepReadonly<string>
    string
  • DeepReadonly<{ f: () => void }>
    { readonly f: () => void }

How a check is judged Exact type equality, not assignability — an intersection is not the same as the flattened object.

How everyone did

Nobody is counting yet. Score distribution and the short game board arrive with accounts — until then your results stay on this device.

Archive
02

Annotated solution

Published July 24, 2026

The solution

type DeepReadonly<T> = T extends (...args: never[]) => unknown
  ? T
  : T extends object
    ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
    : T

The common wrong answer

type DeepReadonly<T> = T extends object
  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : T

Without the function guard, `() => void` matches `extends object` and gets mapped over. A function type has no own enumerable properties, so the result is `{}` — the callable signature is lost.

Line by line

  1. T extends (...args: never[]) => unknown

    `never[]` in parameter position accepts any signature because parameters are checked contravariantly. This is the safe way to say "any function" without `any`.

  2. { readonly [K in keyof T]: DeepReadonly<T[K]> }

    The `readonly` modifier is added while mapping, and the value type recurses. One mapped type handles both jobs.

Takeaway

Conditional branches are tested in order, so narrower cases go first. `extends object` is broader than most people expect — arrays and functions both satisfy it.