--- id: wiki-2026-0508-as-const title: as const category: 10_Wiki/Topics status: verified canonical_id: self aliases: [as const, const assertion, TypeScript const assertion] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [typescript, types, const-assertion, literal-types] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: typescript-5 --- # as const ## 매 한 줄 > **"매 TypeScript 의 const assertion — literal type 으로 widening 차단"**. 매 TS 3.4 (2019) 도입. 매 array → readonly tuple, object → readonly with literal types. 매 enum 대안, discriminated union 의 핵심, type-safe routing/config 의 enabler. ## 매 핵심 ### 매 widening behavior - 매 default: `const x = "hello"` → 매 type `string` (literal type widened). - 매 wait, `const` 변수는 literal 유지: `const x = "hello"` → `"hello"`. 매 그러나 object property 는 widened: `{ name: "alice" }` → `{ name: string }`. - `as const` 의 효과: 매 모든 nested literal 유지 + readonly + tuple inference. ### 매 변환 규칙 - **String/number/boolean literal**: 매 widened type 매 → literal type. - **Array `[1, 2, 3]`**: 매 `number[]` → `readonly [1, 2, 3]` (tuple). - **Object `{ a: 1 }`**: 매 `{ a: number }` → `{ readonly a: 1 }`. - **Nested**: 매 deep recursive 적용. ### 매 응용 1. **Discriminated unions**: 매 `kind: "circle" as const` 로 narrow. 2. **Action creators (Redux)**: 매 type 이 literal string 으로 inferred. 3. **Route configs**: 매 path 가 literal string 으로 inferred → type-safe `Link to=`. 4. **Tuple returns**: 매 `return [value, setter] as const` — React custom hook pattern. 5. **enum 대안**: 매 `const Status = { Idle: "idle", Loading: "loading" } as const`. ## 💻 패턴 ### Tuple return — custom hook ```ts function useToggle(initial = false) { const [on, setOn] = useState(initial); const toggle = useCallback(() => setOn(o => !o), []); return [on, toggle] as const; // 매 type: readonly [boolean, () => void] // 매 without `as const`: (boolean | (() => void))[] — useless union } const [isOpen, toggle] = useToggle(); // 매 properly typed ``` ### Discriminated union via const ```ts const success = (data: string) => ({ kind: "success", data } as const); const error = (msg: string) => ({ kind: "error", msg } as const); type Result = ReturnType | ReturnType; // { readonly kind: "success"; readonly data: string } // | { readonly kind: "error"; readonly msg: string } function handle(r: Result) { if (r.kind === "success") { r.data; // string } else { r.msg; // string } } ``` ### Enum 대안 (lighter, tree-shakeable) ```ts const Status = { Idle: "idle", Loading: "loading", Success: "success", Error: "error", } as const; type Status = typeof Status[keyof typeof Status]; // 매 "idle" | "loading" | "success" | "error" function setStatus(s: Status) { /* ... */ } setStatus(Status.Loading); // ok setStatus("loading"); // ok — literal type setStatus("invalid"); // 매 type error ``` ### Type-safe config / route table ```ts const routes = { home: "/", user: "/user/:id", settings: "/settings", } as const; type RouteKey = keyof typeof routes; type RoutePath = typeof routes[RouteKey]; // 매 "/" | "/user/:id" | "/settings" function Link({ to }: { to: K }) { return ...; } ``` ### Array of literals → union ```ts const SIZES = ["sm", "md", "lg", "xl"] as const; type Size = typeof SIZES[number]; // 매 "sm" | "md" | "lg" | "xl" function Button({ size }: { size: Size }) { /* */ } // 매 runtime + type 동시 derive — 매 single source of truth. SIZES.forEach(s => console.log(s)); // runtime iterable ``` ### Action creators (Redux Toolkit 이전 패턴) ```ts const incrementAction = (n: number) => ({ type: "counter/increment", payload: n, } as const); type IncrementAction = ReturnType; // { readonly type: "counter/increment"; readonly payload: number } ``` ### Deep readonly via as const ```ts const config = { api: { baseUrl: "https://api.example.com", timeout: 5000, }, features: ["auth", "billing"], } as const; config.api.baseUrl = "..."; // 매 error: readonly config.features.push("new"); // 매 error: readonly config.api.baseUrl; // type: "https://api.example.com" (literal) ``` ### satisfies + as const (TS 4.9+) ```ts const palette = { red: "#ff0000", blue: "#0000ff", green: "#00ff00", } as const satisfies Record; palette.red; // type: "#ff0000" — literal preserved // 매 satisfies validates shape, as const preserves literals. ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Custom hook tuple return | `as const` 필수. | | Enum 대체 | `as const` object + `typeof[keyof]`. | | Config / route table | `as const` + `satisfies` (TS 4.9+). | | Discriminated union | `kind: "x" as const` literal narrow. | | Mutable runtime data | 매 `as const` 사용 X — readonly 가 방해. | **기본값**: 매 const literal 유지 필요 시 `as const`. 매 type validation 까지 매 `as const satisfies`. ## 🔗 Graph - 부모: [[TypeScript]] · [[TypeScript 타입 시스템 (TypeScript Type System)|Type System]] - 변형: [[Satisfies Operator]] · [[Readonly]] - 응용: [[Discriminated_Unions|Discriminated Unions]] · [[Custom Hooks]] · [[Redux Toolkit]] - Adjacent: [[Literal Types]] · [[Branded Types for Nominal Typing]] ## 🤖 LLM 활용 **언제**: custom hook tuple return, enum 대체, action creator, route/config table 의 type-safety, discriminated union narrowing 의. **언제 X**: 매 mutable 한 runtime data (readonly 가 방해), 매 simple primitive 의 (`const x = 1` 매 already literal). ## ❌ 안티패턴 - **`as` cast 와 혼동**: 매 `as const` 매 special — `as Foo` (type assertion) 와 다름. - **Mutable assumption**: 매 `as const` array 매 readonly — `.push()` X. `[...arr]` spread 후 mutate. - **Object 의 partial as const**: 매 nested object 의 일부만 const 의도 — 매 not possible. 매 전체 deep 매 readonly. - **enum + as const 동시**: 매 redundant. enum 자체로 literal type. - **JSON.stringify 의 사용**: 매 JSON 화하면 readonly 손실 — runtime 에서 의미 없음. ## 🧪 검증 / 중복 - Verified (TypeScript Handbook, TS 3.4 release notes). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — const assertion patterns, enum 대안, discriminated union, satisfies combo 추가 | ## 🛠️ 적용 사례 (Applied in summary) ### 🔎 코드베이스 근거 (자동 추출 — E:\Wiki 레포) **실제 구현/사용 위치:** - `photoai/src/shared/constants.ts:4` — export const SUPPORTED_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.webp'] as const - `photoai/src/preload/inference.ts:13` — ] as const - `photoai/src/main/menu.ts:107` — type: 'radio' as const, - `connectai/src/features/hire/hireStore.ts:26` — export const KNOWN_STAGES = ['inbox', 'screened', 'interview', 'final', 'offer', 'accepted', 'hired', 'rejected', 'decli - `connectai/src/features/datacollect/handlers.ts:117` — for (const part of [1, 2, 3] as const) { _자동 생성: code_grounding.mjs · 재실행 시 갱신됨_