infer로 타입스크립트의 추론 직접 활용

infer 예약어 with Conditional Type

타입스크립트의 타입 추론 기능을 극한까지 활용하는 기능

// 배열이 있을 때 배열의 요소 타입을 얻어내고 싶은 상황
type El<T> = T extends (infer E)[] ? E : never;
type Str = El<string[]>; // string
type NumOrBool = El<(number | boolean)[]>; // number | boolean
  • 추론을 맡기고 싶은 부분infer 타입 변수로 표

  • 컨디셔널 타입에서 타입 변수는 참 부분에서만 쓸 수 있다.

  • 타입스크립트는 많은 부분을 스스로 추론할 수 있는데, 추론하려는 부분을 infer로 만들면 된다.

    • 매개변수, 생성자 매개변수, 반환값, 인스턴스 타입 등

type MyParameters<T> = T extends (...args: infer P) => any ? P : never;
type MyConstructorParam<T> = T extends abstract new (...args: infer P) => any ? P : never
type MyReturnType<T> = T extends (...args: any) => infer R ? R : any
type MyInstanceType<T> = T extends abstract new (...args: any) => infer R ? R : any

type P = MyParameters<(a: string, b: number) => string> // [a: string, b: number]
type R = MyReturnType<(a: string, b: number) => string> // string
type CP = MyConstructorParam<new (a: string, b: number) => {}> // [a: string, b: number]
type I = MyInstanceType<new (a: string, b: number) => {}> // {}
  • 서로 다른 타입 변수를 여러 개 동시에 사용 가능

type MyPAndR<T> = T extends (...args: infer P) => infer R ? [P, R] : never;
type PR = MyPAndR<(a: string, b: number) => string>;//[[a: number, b: string], string]
  • 같은 타입 변수를 여러 곳에 사용 가능

  • 같은 이름의 타입 변수는 서로 유니온이 된다.

  • 반대로 매개변수의 경우 인터섹션이 된다. (반공변성)

type Union<T> = T extends { a: infer U, b: infer U } ? U : never;
type Result1 = Union<{ a: 1 | 2, b: 2 | 3 }>; // 1 | 2 | 3

type Intersection<T> = T extends {
  a: (pa: infer U) => void,
  b: (pb: infer U) => void,
} ? U : never;
type Result2 = Intersection<{ a(pa: 1 | 2): void, b(pb: 2 | 3): void }>; // 2

매개변수에 같은 타입 변수를 선언하면 인터섹션이 된다.

  • 유니언을 인터섹션으로 만드는 타입

  • U는 제너릭이자 유니언이므로 컨디셔널 타입에서 분배법칙 발생

type UnionToIntersection<U>
  = (U extends any ? (p: U) => void : never) extends (p: infer I) => void
    ? I
    : never
    
type Result = UnionToIntersection<{ a: number } | { b:string }>; //{a:number} & {b:string}
type Result2 = UnionToIntersection<boolean | true>; // never;

Last updated