카테고리 없음

[TypeScript] "T extends U ? X : Y"에 대한 고찰

mugglim 2022. 6. 7. 17:09

TL;DR

  • 타입은 가능한 값의 집합이다.
  • 서브 타입과 슈퍼 타입을 집합의 관계로 설명할 수 있다.
  • 조건부 타입은 분배법칙을 허용한다.

타입은 집합

타입은 가능한 값의 집합이다.

타입은 가능한 값의 집합? 직관적으로 한 번에 이해가 잘 되지 않는다. 예시를 통해 타입과 집합 관계를 이해해보자.

never 타입은 어떠한 값도 할당할 수 없는 타입이다. 어떠한 값도 할당할 수 없기 때문에 공집합이라고 말할 수 있으며, never = {}라고 표기할 수 있다.

예를 하나 더 들어보자. number 타입에는 정수, 소수를 할당할 수 있다. 그러면 number = {정수, 소수}라고 표기할 수 있다.

서브타입과 슈퍼 타입

집합 A가 집합 B의 부분 집합이면 A ⊂ B라고 표기할 수 있다. 두 집합이 서로 같을 때에도 부분 집합 관계라고 한다. 타입은 집합이라고 했으니, 집합의 개념을 타입에 적용할 수 있다. 타입스크립트에서는 타입 간 관계를 설명하는 서브 타입과 슈퍼 타입의 개념이 존재한다. 타입 A와 B가 A ⊂ B 관계를 가질 때, A는 B의 서브타입이고 B는 A의 슈퍼타입이라고 할 수 있다. 단, 수학의 부분 집합 개념과 달리 A와 B가 서로 달라야 한다. 이 부분을 명확하기 위해 아래와 같은 표기법으로 타입 간의 관계를 이해해보자. (타입스크립트 프로그래밍 책에서 사용하는 표기법이다.)

  • A < B : A는 B의 서브 타입이다. (B는 A의 슈퍼 타입이다.)
  • A <: B : A는 B와 같거나, B의 서브 타입이다. (B는 A와 같거나 A의 슈퍼 타입이다.)

예시)

  • 1은 number의 서브 타입 이다. => 1 < number
  • "Hello World"는 stirng의 서브 타입이다. => "Hello World" < string
  • tuple은 array의 서브 타입이다. tuple < array

조건부 타입

T extends U ? X : Y

조건부 타입은 타입스크립트 2.8 버전부터 사용할 수 있는 문법이다. 사용 방법은 T extends U ? X : Y로 표기하며, T <: U 관계이면 X를, 아니면 Y 타입을 반환한다.

조건부 타입과 분배 법칙

T extends U ? X : Y에서 T가 유니온 타입이면 분배 법칙이 적용된다. (핸드북 참고)

예로, 간단한 문제를 풀어보자.

T에서 U에 할당할 수 있는 타입을 제외하는 내장 제네릭 MyExclude<T, U>를 이를 사용하지 않고 구현하세요. (출처: https://github.com/type-challenges/type-challenges/blob/main/questions/00043-easy-exclude/README.ko.md)

문제는 타입 T와 U의 차집합을 구하는 것이다. 타입 T가 "a" | "b" | "c"이고 , U가 "a"라고 생각해보자. T는 유니온 타입이니 조건부 타입에서 분배 법칙이 사용된다. T의 각 타입이 U에 할당될 수 있는지 확인하고 할당될 수 있다면 공집합 인 never 타입을, 할당될 수 없다면 해당 타입을 반환해주면 된다. 이를 정리하면 type MyExclude<T, U> = T extends U ? never : T;라고 정의할 수 있다.

/*
    e.g) 
    Set T is "a" | "b" | "c" and U is "a"

    = T extends U ? never : T
    = ("a" extends "a" ? never : "a") 
	  | ("b" extends "a" ? never : "b")
      | ("c" extends "a" ? never : "c")
    = never | "b" | "c"
    = "b" | "c"
*/
type MyExclude<T, U> = T extends U ? never : T;