Typescript의 유틸리티 타입 (4)

eeensu·2024년 8월 12일

Typescript

목록 보기
22/25
post-thumbnail

ReturnType<T>

ReturnType 타입은 함수의 리턴값의 타입을 지정해주는 유틸리티 타입이다. Generic 인자로 typeof 키워드와 함께 추론할 함수를 넣어주면, 해당 함수의 리턴 타입을 반환한다.

type T0 = ReturnType<() => string>							   // string
type T1 = ReturnType<(s: string) => void>					   // void
type T2 = ReturnType<<T>() => T>							   // unknown
type T3 = ReturnType<<T extends number[]>(...args: T) => T>; // number[]

Functuon 타입을 인자로 전달하면 다음의 에러가 발생한다.

type T8 = ReturnType<Function>; 
// Type 'Function' provides no match for the signature '(...args: any): any'.


Parameters<T>

함수 타입에서 매개변수의 타입들을 추출하여 새로운 튜플 타입(tuple type)을 생성시켜준다.

const logDetails = (a: string, b: number): void => {
    console.log(`a: ${a}, b: ${b}`);
};

// Parameters 유틸리티 타입을 사용해 매개변수 타입 추출
type LogDetailsParams = Parameters<typeof logDetails>; // [a: string, b: number]


// wrapperFunction 함수를 화살표 함수로 변환
const wrapperFunction = (...args: LogDetailsParams): void => {
    console.log('Calling logDetails with:', args);
    logDetails(...args);
};


InstanceType<T>

클래스 타입에서 해당 클래스의 인스턴스 타입을 추출하는 데 사용된다. 즉, 특정 클래스의 인스턴스를 생성했을 때 그 인스턴스가 가지게 되는 타입을 얻어낼 수 있다. 이 유틸리티 타입은 클래스 자체가 아닌, 클래스의 인스턴스가 어떤 타입을 가지는지를 알고 싶을 때 유용하다. 하지만 클래스 타입에만 사용 가능하며, 생성자를 정의하지 않은 클래스는 제한된다.

class User  {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    greet() {
        return `Hello, my name is ${this.name}.`;
    }
}

// `ExampleInstanceType`는 `ExampleClass`의 인스턴스 타입이 된다.
type UserType = InstanceType<typeof User>;

const instance: UserType = new User ('John', 30);

console.log(instance.greet()); // 출력: Hello, my name is John.

js에서의 Class 는 두가지 타입을 가진다. 값이면서도 타입이기도 한다. 하지만 이 두가지는 전혀 다른 걸 가르킨다.

  • User 클래스는 인스턴스 의 타입이고 (new User()로 만들어지는 객체의 타입)
  • typeof User 생성자 타입이다. 이 클래스를 new 로 호출할 수 있다는 그 자체의 타입이다.

그렇다면, 보통은 그냥 User 타입을 그냥 쓰면 되지 않나? 왜 굳이 InstanceType<typeof User> 이렇게 쓰는 거지? 라고 할 수 있다. 하지만 "클래스를 변수로 받아서" 동작하는 제너릭 코드에서는 말이 달라진다.

const create = <T extends new (...args: any[]) => any>(
  ctor: T,
  ...args: ConstructorParameters<T>
): InstanceType<T> => {
  return new ctor(...args);
};

// 사용법
class Dog {
  constructor(public name: string) {}
  bark() {
    console.log(`${this.name} says woof`);
  }
}

const d = create(Dog, 'Buddy'); // d: Dog
d.bark(); // Buddy says woof

create 함수의 흐름은 다음과 같다.

  • T는 클래스 생성자 타입
  • ConstructorParameters 는 생성자의 인자를 추출해주는 유틸리티 타입

하지만 대부분의 상황에선, 클래스 그 자체를 타입으로 쓰기에 InstanceType 을 사용하는 경우는 많지 않다. 사실 위의 예제는 저 유틸리티 타입을 어거지로 쓰는 느낌이 없지않아 있긴 하다..

profile
안녕하세요! 프론트엔드 개발자입니다! (2024/03 ~)

0개의 댓글