chore(brain): ASTRA 성장 자산 동기화 — 기능 인벤토리·growth(약점프로필/학습큐)·일화기억·장기기억·회의록 원문
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
---
|
||||
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 |
|
||||
Reference in New Issue
Block a user