"Compiler 가 너가 case 잊었을 때 말해줘. 옳은 방식으로 물어봐야 해."
Exhaustive-switch 문제
Discriminated union 에 switch 할 때, 새 variant 추가하고 case 잊으면 compiler 가 말해주길 원해. Default 로 TypeScript 가 return 타입 선언했을 때만 잡아 — 그리고 그것도 가끔만. 견고한 해결은 never 트릭: default 분기에서 narrow 된 값을 `never` 타입 변수에 assign. Union 에 처리 안 한 variant 있으면, 그 변수의 실제 타입이 `never` 아니고, assignment 가 compile 시점에 실패.
패턴
function area(s: Shape): number {
switch (s.kind) {
case 'circle': return Math.PI * s.radius ** 2;
case 'square': return s.side ** 2;
default:
const _exhaustive: never = s;
throw new Error(`Unhandled shape: ${JSON.stringify(s)}`);
}
}
`_exhaustive: never` assignment 이 함정. `s` 가 완전히 `never` 로 narrow 됐으면 (모든 variant 처리), assignment 성공. 새 variant 존재하면, `s` 가 여전히 그 variant 타입 — `never` 아님 — 그리고 assignment 에러. Compile 에러가 어느 case 빠졌는지 정확히 말해줘.
왜 이게 killer feature
Refactor-safe 한 discriminated union. `Shape` 타입에 새 variant 추가하면 never 트릭 쓰는 모든 switch 가 켜져. Compile 에러가 update 해야 할 모든 자리의 worklist. 조용한 fallthrough 없음. "어, 이 case 는 X 해야 했는데 잊었어" 없음. Compiler 가 완전성의 포괄적 checker 가 돼.
깔끔하게 만드는 helper
작은 utility 가 깨끗하게 정리: function assertNever(x: never): never { throw new Error(`Unhandled: ${x}`); }. 그러면 default 분기가 default: assertNever(s); 됨 — 한 줄, 같은 안전. cwkPippa frontend 가 standard utility 에 이 helper 가지고 모든 discriminated switch 에 써.