// 조건부 타입
type StringNumberSwitch<T> = T extends number ? string : number;
let a: StringNumberSwitch<number>;
let b: StringNumberSwitch<string>;
타입 변수에 다음과 같이 Union
타입을 할당해 보자.
type StringNumberSwitch<T> = T extends number ? string : number;
(...)
let c: StringNumberSwitch<number | string>;
// string | number
조건부 타입 문법에 따라 number | string
은 number
의 서브타입이 아니므로 조건식이 거짓이 되어 변수 c
의 타입이 number
가 될거라고 예상할 수도 있다.
그러나, 변수 c
는 string | number
타입으로 정의된다.
그 이유는 조건부 타입의 타입 변수에 Union
타입을 할당하면 분산적인 조건부 타입으로 조건부 타입이 업그레이드 되기 때문이다.
분산적인 조건부 타입은 다음과 같이 동작한다.
타입 변수에 할당한 Union 타입 내부의 모든 타입이 분리된다. 따라서
StringNuberSwitch<number | string>
타입은 다음과 같이 분산된다.
StringNumberSwitch<number>
StringNumberSwitch<string>
그리고 다음으로 분산된 각 타입의 결과를 모아 다시
Union
타입으로 묶는다.
- 결과 :
number | string
let d: StringNumberSwitch<boolean | number | string>;
// 1단계
// StringNumberSwitch<boolean> |
// StringNumberSwitch<number> |
// StringNumberSwitch<string>
// 2단계
// number |
// string |
// number
// 결과
// number | string
분산적인 조건부 타입의 특징을 이용하면 매우 다양한 타입을 정의할 수 있다.
예를 들어, Union
타입으로부터 특정 타입만 제거하는 Exclude(제외하다)
타입을 다음과 같이 정의할 수 있다.
type Exclude<T, U> = T extends U ? never : T;
type A = Exclude<number | string | boolean, string>;
위 코드는 다음의 흐름으로 동작한다.
1. Union 타입이 분린된다.
Exclude<number, string>
Exclude<string, string>
Exclude<boolean, string>
2. 각 분리된 타입을 모두 계산한다.
T = number, U = string
일 때number extends string
은 거짓이므로 결과는number
T = string, U = string
일 때string extends string
은 참이므로 결과는never
T = boolean, U = string
일 때boolean extends string
은 거짓이므로 결과는boolean
3. 계산된 타입들을 모두 Union으로 묶는다
- 결과 :
number | never | boolean
계산 결과 타입 A는number | never | boolean
타입으로 정의되는데, 이때 공집합을 의미하는never
타입은 Union으로 묶일 경우 사라진다. 공집합과 어떤 집합의 합집합은 그냥 원본 집합이 되기 때문이다.
따라서, 최종적으로 타입 A
는 number | boolean
타입이 된다.
type Extract<T, U> = T extends U ? T : never;
type B = Extract<number | string | boolean, string>;
분산적인 조건부 타입이 되지 않도록 막고 싶다면 extends
의 양 옆에 대괄호를 씌워주면 된다.
type StringNumberSwitch<T> = [T] extends [number] ? string : number;
let d: StringNumberSwitch<boolean | number | string>;
// 결과 number
boolean | number | string
Union 타입은 extends number
가 거짓이 되므로 최종적으로 그냥 number
가 되는 것이다.