조건부 타입은xtends와삼항 연산자를 이용해 조건에 따라 각각 다른 타입을 정의하도록 하는 문법이다.
type A = number extends string ? number : string
number extends string이 참이면 number타입, 거짓이면 string타입으로 A의 타입이 결정되는 조건부 타입이다.
number extends string은 number타입이 string타입의 서브타입이 아니기 때문에 거짓이 되므로 string타입이 된다.
객체 타입을 이용해서 조건부 타입을 만들 수 있다.
type ObjA = {
a: number
}
type ObjB = {
a: number
b: number
}
type B = ObjB extends ObjA ? number : string
조건부 타입은 제네릭 문법과 같이 사용하면 더 유용하게 쓸 수 있다.
예를 들어,
하는 타입을 만들기 위해서는
type StringNumberSwitch<T> = T extends number ? string : number
let varA: StringNumberSwitch<number> // string
let varB: StringNumberSwitch<string> // number
이런식으로 제네릭을 이용해서 구현할 수 있다.
만약 조건부 타입에 유니온 타입을 할당하면 어떻게 되는지 확인해보자.
type StringNumberSwitch<T> = T extends number ? string : number;
let c: StringNumberSwitch<number | string> // string | number
number | string타입은 number타입의 서브타입이 아니기 때문에 거짓이므로 number타입이 될거라 생각했지만, 정작 c의 타입은 string | number타입이 되었다.
왜냐하면 조건부 타입의 타입변수로 유니온타입이 들어가게 된다면 분산적으로 동작하기 때문이다.
type StringNumberSwitch<T> = T extends number ? string : number;
let c: StringNumberSwitch<number | string>;
// 1. StringNumberSwitch<number> ==> string
// 2. StringNumberSwitch<string> ==> number
// 1과 2의 유니온 타입인 string | number 타입이 된다.
이렇듯
number | string타입은 각각number타입의 결과 타입과string타입의 결과 타입의 유니온 타입으로 동작하는 것이분산적 조건부 타입이다.
infer는 조건부 타입 내에서 특정 타입을 추론하는 문법이다.
특정 함수의 반환값 타입을 추출하는 조건부 타입을 만들려고 할 때 infer를 사용할 수 있다.
type ReturnType<T> = T extends () => infer R ? R : never
type FuncA = () => string
type FuncB = () => number
type A = ReturnType<FuncA> // string
type B = ReturnType<FuncB> // number
T extends () => infer R조건식은 infer R이 조건식을 참으로 만들 수 있는 최적의 R타입을 추론한다는 의미를 말한다.
type A 같은 경우에 타입변수 T로 들어온 () => string타입이 () => infer R 조건식을 참으로 만드는 타입은 R이 string으로 되어야 참이 되므로 R이 string으로 추론이 되는 방식이라고 할 수 있다.
또 다른 예시로 배열 타입의 요소를 추출하는 InferTypeArray타입을 구현한다고 하면
type InferTypeArray<T> = T extends (infer R)[] ? R : never
let numberArr: InferTypeArray<[1, 2, 3]> // number
타입변수 T로 들어온 [1, 2, 3]은 number[]타입이므로 number[] extends (infer R)[] 이 참이 되는 최적의 조건은 R이 number가 되는 조건이므로 R이 number가 된다.
이렇듯 내가 얻고자 하는 타입을 infer R로 바꾸는 식으로 하면 비교적 쉽게 구현할 수 있다.