판별 유니언(Discriminated Union)은 모든 멤버가 리터럴 타입의 공통 필드(구분자(discriminant), 보통 type·kind·status)를 가진 유니언이다. 그 필드를 검사하면 TypeScript가 나머지 속성까지 자동으로 좁혀 준다.
type State =
| { status: 'loading' }
| { status: 'success'; data: User[] }
| { status: 'error'; error: Error }
function render(s: State) {
switch (s.status) {
case 'loading': return '로딩 중'
case 'success': return s.data.length // data 접근 가능
case 'error': return s.error.message
default:
const _exhaustive: never = s // 철저성 검사
return _exhaustive
}
}- 불가능한 상태를 표현할 수 없게 만든다.
{ loading?: boolean; data?: User[]; error?: Error }는 "로딩 중인데 데이터도 있는" 이상한 조합을 허용한다. 판별 유니언은 가능한 상태만 적는다 - 철저성 검사(Exhaustiveness Checking): 새 멤버(
{ status: 'empty' })를 추가하고 switch를 안 고치면never에 대입할 수 없다는 컴파일 에러가 난다. 이렇게 빠진 분기를 빌드 단계에서 찾는다 - React의 비동기 상태, 인증 훅 반환값, useReducer의 액션(
{ type: 'set'; payload: number })에 잘 맞는다 - 같은 구조의 switch가 여러 곳에 반복되면 다형성이나 상태 패턴을 검토한다
좁히기 일반은 타입 좁히기와 타입 가드, 유니언 자체는 유니언·인터섹션·리터럴 타입.