뒤로가기

TypeScript Infer 키워드: 조건부 타입에서의 타입 추론

typescript

TypeScript를 사용하다 보면 더욱 정교한 타입 시스템의 필요성을 느끼게 됩니다. 특히, 복잡한 제네릭 타입을 다룰 때는 조건부 타입(Conditional Types)과 함께 infer 키워드가 매우 유용하게 사용될 수 있습니다.

이 글에서는 TypeScript의 infer 키워드가 무엇인지, 그리고 어떻게 활용할 수 있는지에 대해 살펴보겠습니다.

infer란 무엇인가

infer는 TypeScript의 조건부 타입에서 사용되는 키워드로, 특정 타입의 일부를 추론할 수 있도록 도와줍니다. infer는 제네릭 타입을 분석하여 새로운 타입을 추출하는 역할을 합니다.

조건부 타입의 extends 절 내에서만 사용할 수 있으며, 타입 패턴 매칭을 통해 원하는 부분의 타입을 추출합니다.

기본 사용 예제

먼저, infer의 기본적인 사용법을 살펴보겠습니다. 여기서는 함수의 반환 타입을 추론하는 예제를 사용합니다.

type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

위 코드에서 GetReturnType 타입은 제네릭 T가 함수 타입인 경우, 그 함수의 반환 타입을 추론하여 R로 설정합니다. 만약 함수 타입이 아니라면 never를 반환합니다.

이 타입을 사용하면 특정 함수의 반환 타입을 자동으로 추론할 수 있습니다.

function greet(): string {
  return "Hello";
}
 
type GreetReturn = GetReturnType<typeof greet>; // string

실전 활용 예제

이제 infer 키워드를 실전에서 어떻게 활용할 수 있는지 몇 가지 예제를 통해 살펴보겠습니다.

1. 함수 인수 타입 추출

infer를 사용하면 함수의 인수 타입을 추출할 수 있습니다.

type GetArguments<T> = T extends (...args: infer A) => any ? A : never;
 
type Args1 = GetArguments<(x: number, y: string) => void>; // [number, string]
type Args2 = GetArguments<() => void>; // []

이 코드에서 GetArguments 타입은 주어진 함수의 인수 타입을 튜플로 반환합니다. 이를 통해 함수의 인수 타입을 쉽게 추출할 수 있습니다.

2. 프로미스의 반환 타입 추출

infer는 프로미스 타입의 반환 값을 추론하는 데도 매우 유용합니다.

type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
 
type Example1 = UnwrapPromise<Promise<string>>; // string
type Example2 = UnwrapPromise<number>; // number

UnwrapPromise 타입은 프로미스에서 반환되는 실제 타입을 추론합니다. 프로미스 타입이 아닌 경우에는 원래 타입을 그대로 반환합니다.

이는 async/await와 함께 사용될 때 특히 유용합니다:

async function fetchUser(): Promise<{ name: string; age: number }> {
  // ...
}
 
type User = UnwrapPromise<ReturnType<typeof fetchUser>>;
// { name: string; age: number }

3. 배열 요소 타입 추출

배열 타입에서 요소의 타입을 추출하는 방법도 infer로 간단하게 구현할 수 있습니다.

type ElementType<T> = T extends (infer U)[] ? U : T;
 
type Example1 = ElementType<string[]>; // string
type Example2 = ElementType<number[]>; // number
type Example3 = ElementType<boolean>; // boolean

ElementType 타입은 배열의 요소 타입을 반환하며, 배열이 아닌 타입이 주어질 경우 그 타입 자체를 반환합니다.

4. 튜플에서 특정 위치의 타입 추출

튜플 타입에서 첫 번째 요소의 타입을 추출할 때도 infer를 사용할 수 있습니다.

type First<T> = T extends [infer U, ...any[]] ? U : never;
 
type Example1 = First<[string, number, boolean]>; // string
type Example2 = First<[boolean, number]>; // boolean

First 타입은 튜플의 첫 번째 요소 타입을 추출하여 유용하게 활용할 수 있습니다.

마지막 요소를 추출하는 것도 가능합니다:

type Last<T> = T extends [...any[], infer U] ? U : never;
 
type Example1 = Last<[string, number, boolean]>; // boolean

고급 활용 패턴

중첩된 타입 추론

infer를 중첩하여 더 복잡한 타입 추론도 가능합니다:

type DeepUnwrap<T> = T extends Promise<infer U>
  ? DeepUnwrap<U>
  : T;
 
type Example = DeepUnwrap<Promise<Promise<Promise<string>>>>; // string

조건부 타입과 결합

infer는 여러 조건부 타입과 결합하여 강력한 타입 유틸리티를 만들 수 있습니다:

type FlattenArray<T> = T extends (infer U)[]
  ? U extends (infer V)[]
    ? V
    : U
  : T;
 
type Example1 = FlattenArray<string[][]>; // string
type Example2 = FlattenArray<number[]>; // number

결론

TypeScript의 infer 키워드는 조건부 타입과 함께 사용하여 복잡한 타입 추론을 가능하게 합니다. 함수의 반환 타입, 인수 타입, 프로미스의 언래핑, 배열 요소 타입 추출 등 다양한 상황에서 활용할 수 있습니다.

infer를 효과적으로 활용하면 타입 안전성을 유지하면서도 유연한 타입 시스템을 구축할 수 있습니다. 특히 라이브러리나 프레임워크를 개발할 때, infer를 사용하면 사용자에게 더 나은 타입 추론 경험을 제공할 수 있습니다.

관련 아티클