TypeScript Snippet

DeepReadonly Utility Type

Difficulty: Medium

`Readonly<T>` only freezes the top level, which is rarely enough for state objects passed across module boundaries. `DeepReadonly<T>` walks the type tree and marks every property `readonly` at every depth. This snippet builds the recursive form, refines it for arrays so they become `ReadonlyArray<T>`, and pairs it with `Object.freeze` so the runtime matches the type-level guarantee.

Code Snippets
/

DeepReadonly Utility Type

DeepReadonly Utility Type

`Readonly<T>` only freezes the top level, which is rarely enough for state objects passed across module boundaries. `DeepReadonly<T>` walks the type tree and marks every property `readonly` at every depth. This snippet builds the recursive form, refines it for arrays so they become `ReadonlyArray<T>`, and pairs it with `Object.freeze` so the runtime matches the type-level guarantee.

TypeScript
Medium
3 snippets
ts-utility-types
ts-mapped-types
ts-conditional-types
code-template

501 views

9

Adding readonly inside the mapped type rebuild marks every property on the new shape as immutable. The conditional T extends object ? ... : T stops the recursion at primitives, just like the DeepPartial pattern. The cast in the runtime sentinel is for testing only; in real code the value comes from a function that returns DeepReadonlyBasic<T> so the compiler tracks immutability transitively. Note that readonly is a compile-time check only; it does not freeze the object at runtime, which the third accordion fixes.

DeepReadonly Utility Type | Code Snippets | CodeSnatch