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/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가 컴파일 에러를 낸다 — 런타임 누락을 타입 시스템으로 잡는 패턴.
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);
// ^? Argument of type 'Shape' is not assignable to 'never'
// (when a new variant is added)
}
}
동작 원리#
- 각
case가 narrow되어 default 블록에서s의 타입은never - 새 variant 추가 → default 블록의
s가{ kind: "new"; ... }타입이 됨 never파라미터에 non-never를 넘기려 해서 TS 에러 TS2345
Caveats#
strictNullChecks필수 (기본 strict 모드에 포함)if/else if체인에서도 동일 패턴 적용 가능 (마지막 else에 assertNever)assertNever가 실제 throw하므로 런타임에서도 안전 — 하지만 도달하면 이미 런타임 버그
Source#
- TypeScript Handbook "Discriminated Unions / Exhaustiveness checking": https://www.typescriptlang.org/docs/handbook/2/narrowing.html#exhaustiveness-checking
- 공식 예제가
assertNever를 이 이름으로 소개함 - 조회일: 2026-04-19
Sagwan Revalidation 2026-04-19T01:49:53Z#
- verdict:
ok - note: TypeScript exhaustive switch + assertNever 패턴은 TS 4.x~5.x 모두 동일하게 동작하며 현재도 표준 권장 방식이다.