---
id: wiki-2026-0508-deepreadonly
title: DeepReadonly
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [deep-readonly, recursive-readonly, immutable-type]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [typescript, type-utility, immutability]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: typescript
  framework: typescript-5.x
---

# DeepReadonly

## 매 한 줄
> **"매 `Readonly<T>` 의 surface-only — 매 nested mutation 가 still possible"**. 매 DeepReadonly<T> 가 매 recursively 의 every property 의 readonly 의 mark — 매 redux state, config object, frozen domain model 의 essential. TS 5.x 의 매 `const` type parameter + DeepReadonly 가 매 powerful combo.

## 매 핵심

### 매 vs Readonly
- `Readonly<T>` — 매 top-level only. 매 `obj.nested.mutate = X` 가 still allowed.
- `DeepReadonly<T>` — 매 every level 의 recursive freeze.

### 매 type-only vs runtime
- DeepReadonly 의 매 compile-time type guarantee — 매 runtime 의 mutation 의 X protect.
- 매 runtime freeze 의 `Object.freeze` (shallow) 또는 `deepFreeze` helper 사용.
- TypeScript 5.0+ `as const` 의 매 literal-level deep readonly 의 produce.

### 매 사용처
1. Redux/Zustand state shape — 매 mutation prevent.
2. Configuration schemas (env config, feature flags).
3. API response DTOs after parse.
4. Domain entities in DDD value objects.
5. Test fixtures (prevent accidental modification).

## 💻 패턴

### Basic DeepReadonly
```ts
type DeepReadonly<T> = T extends (...args: any[]) => any
  ? T
  : T extends object
    ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
    : T;

interface User {
  id: string;
  profile: { name: string; tags: string[] };
}
const u: DeepReadonly<User> = { id: '1', profile: { name: 'a', tags: ['x'] } };
// u.profile.name = 'b'; // ❌ Cannot assign
// u.profile.tags.push('y'); // ❌ readonly array
```

### Handles Map / Set / Array
```ts
type DeepReadonly<T> =
  T extends (infer U)[] ? readonly DeepReadonly<U>[]
  : T extends Map<infer K, infer V> ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
  : T extends Set<infer U> ? ReadonlySet<DeepReadonly<U>>
  : T extends Promise<infer U> ? Promise<DeepReadonly<U>>
  : T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : T;
```

### Brand-aware (preserve nominal types)
```ts
type Brand<T, B> = T & { readonly __brand: B };
type DeepReadonly<T> = T extends Brand<infer U, infer B>
  ? Brand<DeepReadonly<U>, B>
  : T extends object
    ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
    : T;
```

### Runtime deepFreeze pair
```ts
export function deepFreeze<T>(obj: T): DeepReadonly<T> {
  if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {
    Object.freeze(obj);
    for (const key of Object.keys(obj)) deepFreeze((obj as any)[key]);
  }
  return obj as DeepReadonly<T>;
}
```

### `as const` + DeepReadonly
```ts
const config = {
  features: { newCheckout: true, beta: ['user1', 'user2'] },
  limits: { rpm: 100 },
} as const;
// 매 `as const` 가 매 every literal 의 deeply readonly + literal-typed 로 만듦.
type Config = typeof config;
// Config['features']['beta'] === readonly ['user1', 'user2']
```

### Mutable inverse (DeepWritable)
```ts
type DeepWritable<T> = T extends (...args: any[]) => any
  ? T
  : T extends object
    ? { -readonly [K in keyof T]: DeepWritable<T[K]> }
    : T;

function clone<T>(x: DeepReadonly<T>): DeepWritable<T> {
  return structuredClone(x as T) as DeepWritable<T>;
}
```

### Zod + DeepReadonly
```ts
import { z } from 'zod';
const UserSchema = z.object({ id: z.string(), tags: z.array(z.string()) }).readonly();
type User = DeepReadonly<z.infer<typeof UserSchema>>;
```

### Function args (Redux reducer)
```ts
function reducer<S, A>(state: DeepReadonly<S>, action: A): S {
  // state.x = ... // ❌ compile error
  return { ...(state as S), updated: true } as S;
}
```

## 매 결정 기준
| 상황 | Approach |
|---|---|
| Literal config | `as const` |
| Generic state shape | `DeepReadonly<T>` utility |
| Runtime guarantee 필요 | `deepFreeze` + DeepReadonly |
| Performance hot path | 매 type-only DeepReadonly (no runtime cost) |
| Library author 가 expose | DeepReadonly + readonly arrays in public API |

**기본값**: type-only DeepReadonly + `as const` for literals; runtime `deepFreeze` only for security-sensitive boundaries.

## 🔗 Graph
- 부모: [[TypeScript-Type-System]] · [[Immutability]]
- 변형: [[Readonly]] · [[as-const]]
- 응용: [[Zustand]] · [[Domain-Driven-Design]]
- Adjacent: [[Branded-Types]] · [[Zod]] · [[Structural-Sharing]]

## 🤖 LLM 활용
**언제**: 매 utility type 의 design / extension, 매 type error explanation, 매 readonly violation 의 codemod 작성.
**언제 X**: 매 runtime data validation (Zod 사용). 매 hot-path performance tuning (TS types 가 erased — runtime cost 0).

## ❌ 안티패턴
- **함수 type 의 readonly 적용**: 매 `(...args) => any` 가 readonly 의 의미 X — special-case 필요.
- **Date / RegExp 의 recurse**: 매 built-in instances 가 깨짐 — exclude 의 type guard.
- **DeepReadonly + cast away**: `state as Mutable` 가 매 type safety 의 destroy.
- **Runtime mutation through cast**: 매 `(state as any).x = 1` — 매 type lie 의 propagate.
- **Naive `keyof T` on union**: distributive conditional 의 사용.

## 🧪 검증 / 중복
- Verified (TypeScript 5.x docs, type-fest library, ts-toolbelt).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — DeepReadonly utility type variants and patterns |
