ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TypeScript 타입 추론의 새로운 지평: infer의 매력
    Javascript 2024. 6. 16. 18:29

     

    TypeScript에서 infer는 마치 마법 주문처럼 타입 추론의 범위를 넓혀줍니다.

     

    덕분에 더욱 정확하고 유연한 타입을 정의할 수 있게 되었죠.

     

    하지만 infer는 늘 있었던 것은 아닙니다.

     

    최근에 더욱 주목받는 이유는 무엇일까요?

    infer를 이해하기 위한 여정: Conditional Type부터 시작

    infer를 처음 접했을 때, 저는 갑작스러운 등장에 당황했고, 제대로 활용하는 방법을 몰라 이해하는 데 시간이 걸렸습니다.

     

    infer를 제대로 이해하기 위해 먼저 Conditional Type에 대한 기본적인 내용을 복습했습니다.

     

    아래 코드는 TypeScript 공식 문서에서 가져온 것입니다.

    interface Animal {
      live(): void;
    }
    interface Dog extends Animal {
      woof(): void;
    }
    
    type Example1 = Dog extends Animal ? number : string;
    type Example2 = RegExp extends Animal ? number : string;

     

    이 삼항 연산자처럼 생긴 것이 바로 Conditional Type입니다.

     

    Conditional Type:

    T extends U ? X : Y
    • T extends U는 T 타입이 U 타입의 서브타입(즉, U 타입에 할당 가능)인지 확인합니다.
    • ? X : YT extends U가 true이면 X 타입을 반환하고, 아니면 Y 타입을 반환합니다.

    즉, T 타입이 U 타입에 포함되어 있으면 X 타입을 반환하고, 포함되어 있지 않으면 Y 타입을 반환합니다.

     

    infer의 등장: 타입 추론의 진화

    Conditional Type을 이해한 후, infer에 대한 이해를 다시 시작했습니다.

     

    Conditional Type:

    T extends U ? X : Y
    • inferU 부분에서 사용하는 키워드입니다.

    infer는 TypeScript의 Conditional Type에서 사용되는 특수 키워드입니다.

     

    Conditional Type은 특정 조건을 만족하는지 확인하고, 그 결과에 따라 다른 타입을 반환할 수 있는 기능입니다.

     

    이는 프로그래밍의 if 문과 유사하지만, 타입에 대해 수행합니다.

     

    infer 키워드는 Conditional Type 내에서 사용되며, 특정 타입에서 다른 타입을 "추론"(즉, 추출)하는 데 사용됩니다.

     

    예를 들어, 함수의 반환 값 타입을 알고 싶을 때 infer를 사용할 수 있습니다.

     

    TypeScript 공식 문서의 코드를 다시 살펴보았습니다.

    type GetReturnType<Type> = Type extends (...args: never[]) => infer Return
      ? Return
      : never;
    
    type Num = GetReturnType<() => number>;
    
    type Str = GetReturnType<(x: string) => string>;
    
    type Bools = GetReturnType<(a: boolean, b: boolean) => boolean[]>;

     

    확실히 U 부분이 (...args: never[]) => infer Return으로 되어 있습니다.

     

    위 코드는 Type(T)가 "(인수를 받지 않는) 함수"인지 확인합니다.

     

    Type이 함수이면, 해당 함수의 반환 값 타입을 infer Return으로 추출하여 Return 타입을 반환합니다.

     

    Type이 함수가 아니면 never 타입을 반환합니다.

     

    참고로, 인수를 받는 함수의 경우 ...args: any[]가 됩니다.

     

    덧붙이자면, ...argsarguments 객체로 JavaScript에서도 사용됩니다.

     

    https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/arguments

     

    arguments 객체 - JavaScript | MDN

    arguments 객체는 함수에 전달된 인수에 해당하는 Array 형태의 객체입니다.

    developer.mozilla.org

     

     

    arguments는 배열처럼(Array-like) 동작하는 객체로 함수에 전달된 인수 값을 포함하고 있으며, 함수 내에서 접근할 수 있습니다.

     

    또 다른 샘플 코드를 살펴보았습니다.

     

    다시 한번 TypeScript 공식 문서의 코드를 확인했습니다.

    type Unpacked<T> = T extends (infer U)[]
      ? U
      : T extends (...args: any[]) => infer U
      ? U
      : T extends Promise<infer U>
      ? U
      : T;
    type T0 = Unpacked<string>; // string
    type T1 = Unpacked<string[]>; // string
    type T2 = Unpacked<() => string>; // string
    type T3 = Unpacked<Promise<string>>; // string
    type T4 = Unpacked<Promise<string>[]>; // Promise<string>
    type T5 = Unpacked<Unpacked<Promise<string>[]>>; // string

     

     

    T extends (infer U)[]

     

    T 타입이 배열 타입인지 확인합니다.

     

    배열 타입이면 해당 배열의 요소 타입을 infer U로 추출하고, U 타입을 반환합니다.

    T extends (...args: any[]) => infer U

     

    함수 타입인지 확인합니다.

    T extends Promise

     

    Promise 타입인지 확인합니다.

     

    string 타입은 배열 타입도, 함수 타입도, Promise 타입도 아니기 때문에 그대로 타입을 반환합니다.

     

    그래서 아래 코드는 string을 반환합니다.

    type T0 = Unpacked; // string

     

    infer의 등장: 타입 추론의 새로운 지평을 열다

    infer는 TypeScript 2.8 버전에서 처음 등장했습니다.

     

    이전에는 typeofkeyof 등으로 제한적인 타입 추론만 가능했지만, infer제네릭 타입에서 특정 부분을 추론할 수 있도록 힘을 실어줍니다.

     

    예를 들어, Promise 타입의 T를 추론하는 경우를 살펴봅시다.

    type PromiseType<T> = T extends Promise<infer U> ? U : never; 

     

    Promise<infer U>를 통해 Promise 타입의 제네릭 파라미터 U를 추론하여 PromiseType을 정의할 수 있습니다.

    infer를 활용한 다양한 패턴

    infer의 등장은 TypeScript 개발자들에게 새로운 가능성을 열어주었습니다.

    • 조건부 타입을 활용한 다양한 타입 변환: Promise 타입 뿐 아니라, 다양한 타입과 함께 infer를 사용하여 원하는 타입을 추출하고 변환할 수 있습니다.
    • 복잡한 타입 추론: infer를 활용하여 깊이 중첩된 객체의 타입을 추론하거나, 함수의 인자와 반환 값의 타입을 정확하게 추론할 수 있습니다.
    • 타입 안전성 향상: infer를 통해 타입 추론을 강화함으로써, 개발 중 발생할 수 있는 타입 오류를 미리 방지하고 코드의 안전성을 높일 수 있습니다.

    infer와 함께 발전하는 TypeScript 생태계

    infer의 등장은 TypeScript 생태계에 큰 영향을 미쳤습니다.

    • 타입 유틸리티 라이브러리의 발전: infer를 활용한 다양한 유틸리티 타입이 개발되어, 개발자들이 더욱 편리하게 타입 작업을 수행할 수 있도록 돕고 있습니다.
    • 더욱 강력해진 타입 정의: infer를 통해 더욱 정확하고 유연한 타입을 정의할 수 있게 되면서, TypeScript 코드의 가독성과 유지보수성이 향상되었습니다.

    마무리

    infer는 TypeScript의 타입 추론 기능을 한 단계 끌어올린 핵심 기능입니다.

     

    infer를 활용하면 더욱 정확하고 안전한 TypeScript 코드를 작성할 수 있으며, TypeScript 생태계의 발전에도 크게 기여하고 있습니다.

     

    infer를 활용하여 타입 추론의 즐거움을 경험해 보세요!

Designed by Tistory.