"조건부 타입의 입력이 유니언이면, TypeScript는 종종 전체를 한 번 묻지 않고 각 항목에 같은 질문을 따로 던져."
분배가 일어나는 조건
조건부 타입이 T extends X ? A : B이고 검사하는 T가 가공되지 않은 제네릭 매개변수라면, T에 유니언이 들어왔을 때 각 구성원에 조건을 따로 적용해. 결과는 각 계산 결과의 유니언으로 다시 합쳐진다.
type Wrap<T> = T extends unknown ? { value: T } : never에 string 또는 number를 넣으면 { value: string } | { value: number }가 돼. { value: string | number } 하나와는 다른 모양이야.
유니언 필터가 되는 이유
Exclude<T, U>는 T의 각 구성원을 U에 대입할 수 있는지 검사하고, 맞으면 never, 아니면 원래 구성원을 돌려줘. 유니언에서 never는 사라지므로 원하는 항목만 남는다. NonNullable 같은 표준 도구도 같은 분배 성질을 이용해.
전체를 한 번에 검사하고 싶을 때
분배를 막으려면 양쪽을 한 칸짜리 튜플로 감싸 [T] extends [X]라고 써. 이제 검사 대상이 가공되지 않은 T가 아니라 튜플이므로 전체 유니언을 한 덩어리로 비교해. ‘각 항목이 배열인가?’와 ‘전체 유니언이 배열에 대입 가능한가?’는 다른 질문이야.
왜 결과가 이상해 보이는가
고급 타입에서 예상보다 유니언이 많이 생기면 먼저 분배가 일어났는지 확인해. 반대로 항목별 변환을 기대했는데 결과가 하나라면 T가 객체나 배열 안에 감싸져 분배 조건을 잃었을 수 있어. 규칙을 외우기보다 컴파일러가 전체에 물었는지 각 항목에 물었는지 구별하면 돼.