[타입스크립트] 컨디셔널 타입 분배법칙

ChuYong·2023년 9월 23일
0

컨디셔널 타입 분배법칙

검사하려는 타입이 제네릭이면서 유니언일때 수행된다.
간단하게,
(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 | numberstring을 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에서의 타입 분배법칙

한편 주의해야할 조건이 있다. 바로 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[] 은 다른 타입이다.

따라서 상황에 따라 위의 같은 분배법칙을 막고 싶을 수 있다.

하나의 상황을 더 살펴보자

Never에서의 타입 분배법칙

타입이 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[] 로 추론된다.

profile
백엔드 & 인프라 를 좋아하는 개발자에요

0개의 댓글

관련 채용 정보