[TS] 조건부 타입

DT-HYUNJUN·2023년 12월 18일

Typescript

목록 보기
11/11

조건부 타입

조건부 타입xtends삼항 연산자를 이용해 조건에 따라 각각 다른 타입을 정의하도록 하는 문법이다.

type A = number extends string ? number : string

number extends string이 참이면 number타입, 거짓이면 string타입으로 A의 타입이 결정되는 조건부 타입이다.

number extends stringnumber타입이 string타입의 서브타입이 아니기 때문에 거짓이 되므로 string타입이 된다.

객체 타입을 이용해서 조건부 타입을 만들 수 있다.

type ObjA = {
  a: number
}

type ObjB = {
  a: number
  b: number
}

type B = ObjB extends ObjA ? number : string

제네릭 조건부 타입

조건부 타입은 제네릭 문법과 같이 사용하면 더 유용하게 쓸 수 있다.

예를 들어,

  • number타입이 할당되면 string타입을 반환
  • string타입이 할당되면 number타입을 반환

하는 타입을 만들기 위해서는

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는 조건부 타입 내에서 특정 타입을 추론하는 문법이다.

특정 함수의 반환값 타입을 추출하는 조건부 타입을 만들려고 할 때 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 조건식을 참으로 만드는 타입은 Rstring으로 되어야 참이 되므로 Rstring으로 추론이 되는 방식이라고 할 수 있다.

또 다른 예시로 배열 타입의 요소를 추출하는 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)[] 이 참이 되는 최적의 조건은 Rnumber가 되는 조건이므로 Rnumber가 된다.

이렇듯 내가 얻고자 하는 타입을 infer R로 바꾸는 식으로 하면 비교적 쉽게 구현할 수 있다.

0개의 댓글