검사하려는 타입이 제네릭이면서 유니언일때 수행된다.
간단하게,
(S | T) extends A ? B : C
는 (S extends A ? B : C) | (T extends A ? B : C)
가 된다
조건부 타입일 때 수행된다.
컨디셔널 타입을 언제 사용하면 좋을까?
우선 문제 상황을 살펴보자,
type Something = string | number;
type Result = Something extends string ? Something[] : never;
여기서 Result의 삼항연산자는 Something로 추론할까, never로 추론할까?
결과는 never이다.
string | number
는 string
을 extends 할 수 없기 때문이다.
쉽게 말해서, string | number
는 1을 대입할수도, test라는 문자열을 대입할수도 있겠지만,
그렇기 때문에 그 타입이 string
을 extends 할 수 없는 것이다.
이때 필요한 개념이 “컨디셔널 타입 분배법칙” 이다.
한줄로 요약해서 “유니언 타입을 쪼개는 방법” 이다.
다음 코드를 살펴보자
type Something = string | number;
type SomeGeneric<Key> = Key extends string ? Key[] : never;
type ParsedSomething = SomeGeneric<Something>;
이때 ParsedSomething는 무슨 타입으로 추론될까?
정답은 string[] 으로 추론된다.
왜일까?
조건부 연산 중에,
SomeGeneric<string | never>
이 SomeGeneric<string> | SomeGeneric<never>
로 분배법칙 되어서,
SomeGeneric<string>
는 자연스럽게 string[]
이 되었을 것이고, SomeGeneric<never>
은 그냥 never
가 되어서 string[] | never
가 되어 결과적으로 string[]
이 된것이다.
한편 주의해야할 조건이 있다. 바로 boolean의 컨디셔널 타입 분배법칙인데, 다음을 살펴보자
type SomeGeneric<Key> = Key extends boolean ? Key[] : never;
type ToArray<Key> = Key[];
type A = SomeGeneric<boolean>;
type B = ToArray<boolean>;
위 코드에서 일반적으로 생각해보면 A와 B모두 boolean[]가 나와야 한다.
하지만 A는 true[] | false[]
, B는 boolean[]
가 된다.
위의 컨디셔널 타입 분배법칙에 의해 boolean 은 true | false가 되고, 그래서 SomeGeneric | SomeGeneric 가 되어서 결국 true[] | false[]가 된다.
당연히 B타입은 컨디셔널 타입이 아니기에 분배법칙도 발생하지 않아 그대로 boolean[] 이 된다.
논리적으로 true[] | false[]
와 boolean[]
은 다른 타입이다.
따라서 상황에 따라 위의 같은 분배법칙을 막고 싶을 수 있다.
하나의 상황을 더 살펴보자
타입이 string인지 검사해주는 컨디셔널 타입을 하나 만들어보자
type IsString<T> = T extends string ? true : false;
type MayBeTrue = IsString<'Hello!'>;
type MayBeFalse = IsString<number>;
type MayBeFalse2 = IsString<never>;
여기서 MayBeTrue는 true가, MayBeFalse는 false가, MayBeFalse2는 true가 나올 것 같다.
never는 모든 타입을 extends 할 수 있기에, true가 나올 것 같다.
하지만 MayBeFalse2는 never로 평가된다.
T타입은 T | never과 같다.
왜일까?
다시 처음 분배법칙의 정의를 보면
(S | T) extends A ? B : C
는 (S extends A ? B : C) | (T extends A ? B : C)
가 된다고 했다.
이때, T extends A ? B: C
는 논리적으로 T | never extends A ? B : C
과 동치이다. (T | never = T)
이러면 (T extends A ? B : C) | (never extends A ? B : C)
가 된다.
이는 처음 정의 (T extends A ? B : C)
와 다시 동치여야만 한다.
이때 (T extends A ? B : C) | (never extends A ? B : C)
가 (T extends A ? B : C)
가 같을 수 있는 유일한 조건은 (never extends A ? B : C)
가 never인 경우밖에 없다. never로 평가되지 않으면 애초에 분배법칙이 성립할 수가 없는 것이다.
앞선 두 사례에서처럼 컨디셔널 타입 분배법칙을 의도적으로 막아야 하는 경우가 생긴다.
조건부에 들어가는 제네릭, 타입들을 배열로 감싸주면 된다. 위 상황에서
type SomeGeneric<Key> = [Key] extends [boolean] ? Key[] : never;
type ToArray<Key> = Key[];
type A = SomeGeneric<boolean>;
type B = ToArray<boolean>;
로 바꿔주면 A 역시 boolean[]
로 추론된다.