--- 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` 의 surface-only — 매 nested mutation 가 still possible"**. 매 DeepReadonly 가 매 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` — 매 top-level only. 매 `obj.nested.mutate = X` 가 still allowed. - `DeepReadonly` — 매 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 extends (...args: any[]) => any ? T : T extends object ? { readonly [K in keyof T]: DeepReadonly } : T; interface User { id: string; profile: { name: string; tags: string[] }; } const u: DeepReadonly = { 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 extends (infer U)[] ? readonly DeepReadonly[] : T extends Map ? ReadonlyMap, DeepReadonly> : T extends Set ? ReadonlySet> : T extends Promise ? Promise> : T extends object ? { readonly [K in keyof T]: DeepReadonly } : T; ``` ### Brand-aware (preserve nominal types) ```ts type Brand = T & { readonly __brand: B }; type DeepReadonly = T extends Brand ? Brand, B> : T extends object ? { readonly [K in keyof T]: DeepReadonly } : T; ``` ### Runtime deepFreeze pair ```ts export function deepFreeze(obj: T): DeepReadonly { 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; } ``` ### `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 extends (...args: any[]) => any ? T : T extends object ? { -readonly [K in keyof T]: DeepWritable } : T; function clone(x: DeepReadonly): DeepWritable { return structuredClone(x as T) as DeepWritable; } ``` ### Zod + DeepReadonly ```ts import { z } from 'zod'; const UserSchema = z.object({ id: z.string(), tags: z.array(z.string()) }).readonly(); type User = DeepReadonly>; ``` ### Function args (Redux reducer) ```ts function reducer(state: DeepReadonly, action: A): S { // state.x = ... // ❌ compile error return { ...(state as S), updated: true } as S; } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Literal config | `as const` | | Generic state shape | `DeepReadonly` 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 |