kind: capsule status: active visibility: private license: fair-use-summary summary: Discriminated union switch에서 default에 assertNever(x: never) 추가하면 새 variant 추가 시 컴파일 타임에 누락 감지. tags: - typescript - type-safety - pattern - capsule related: - personal_vault/knowledge/dev/typescript-advanced.md - personal_vault/knowledge/dev/typescript.md - personal_vault/projects/ops/librarian/capsules/TypeScript Practical Reference Capsule.md
TypeScript Exhaustive Switch with never Capsule
Core claim#
Discriminated union을 switch로 분기할 때, default 블록에서 never 타입으로 변수를 소비하는 헬퍼를 호출하면 union에 새 variant가 추가됐을 때 TypeScript가 컴파일 에러를 낸다. 런타임 누락을 타입 시스템으로 잡는 표준적인 exhaustiveness checking 패턴이다.
When to apply#
- redux/zustand action union, API response discriminated union, state machine state
- 도메인이 앞으로 확장될 가능성이 있고, 모든 consumer가 새 케이스를 처리해야 하는 경우
Recipe#
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number }
| { kind: "triangle"; base: number; height: number };
function assertNever(x: never): never {
throw new Error(`Unhandled variant: ${JSON.stringify(x)}`);
}
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "square": return s.side ** 2;
case "triangle": return (s.base * s.height) / 2;
default: return assertNever(s);
}
}
새 variant를 추가하고 case를 빠뜨리면 default 블록의 s가 더 이상 never가 아니므로 assertNever(s)에서 TS2345 계열의 오류가 난다.
동작 원리#
- 각
case가 discriminant(kind) 기준으로 narrow된다. - 모든 variant가 처리되면
default블록에서s의 타입은never가 된다. - 새 variant 추가 후 해당
case를 누락하면default블록의s가 그 새 variant 타입으로 남는다. never파라미터에 non-never 값을 넘기려 하므로 컴파일 에러가 발생한다.
Caveats#
strictNullChecks는 TypeScript의 다른 exhaustive-checking 방식(예: 명시적 반환 타입과 누락 return 감지)에서 특히 중요하지만,assertNever패턴 자체의 핵심 전제는 discriminated union이 올바르게 narrow되는 것이다. 실무에서는strict: true와 함께 쓰는 것을 권장한다.if / else if체인에서도 동일 패턴을 적용할 수 있다. 마지막else에서assertNever(value)를 호출한다.assertNever가 실제로throw하므로 런타임에서도 방어선이 된다. 다만 이 경로에 도달했다면 이미 타입 경계 밖의 값 유입 또는 컴파일 누락이 발생한 버그로 봐야 한다.default없이 모든case를 나열하는 스타일을 선호하는 팀도 있다. 누락 감지를 강하게 보장하려면default의assertNever또는 switch 이후const _: never = value같은 명시적 never 체크를 둔다.
Source#
- TypeScript Handbook, “Discriminated Unions / Exhaustiveness checking”: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking
- Vault cross-check:
personal_vault/knowledge/dev/typescript.md의 “exhaustive check” 예시는default: const _: never = s형태로 같은 원리를 사용한다. - 조회일: 2026-05-13
Sagwan Revalidation 2026-04-19T01:49:53Z#
- verdict:
ok - note: TypeScript exhaustive switch + assertNever 패턴은 TS 4.x~5.x 모두 동일하게 동작하며 현재도 표준 권장 방식이다.
Sagwan Revalidation 2026-04-20T02:27:42Z#
- verdict:
ok - note: TypeScript exhaustive switch + assertNever 패턴은 현재도 표준 관행이며, 코드·설명 모두 정확하고 최신 practice와 일치한다.
Sagwan Revalidation 2026-04-21T02:43:43Z#
- verdict:
ok - note: LLM unavailable: [CLI 오류 1] SessionEnd hook [node "/home/insu/.pixel-agents/hooks/claude-hook.js"] failed: node:internal/modules/cjs/load
Sagwan Revalidation 2026-04-22T03:14:33Z#
- verdict:
ok - note: LLM unavailable: [CLI 오류 1] SessionEnd hook [node "/home/insu/.pixel-agents/hooks/claude-hook.js"] failed: node:internal/modules/cjs/load
Sagwan Revalidation 2026-05-13#
- verdict:
revise - note: 핵심 주장은 유지. Caveats의
strictNullChecks 필수표현을strict: true 권장, assertNever 자체의 필수 조건은 아님으로 완화함.