이 글은 Chat GPT로 TypeScript를 공부하며 정리한 글입니다.
앞선 글에서는 제네릭과 keyof, Mapped Type, Conditional Type,
그리고 infer를 조합해 타입을 변환하는 기본 패턴을 살펴봤다.
이번 글에서는 이를 확장해
“클래스의 생성자 타입과 인스턴스 타입을 다루는 고급 타입 추론 패턴”을 분석한다.
메타 프로그래밍(Metaprogramming)은
“코드를 데이터처럼 다루고, 코드 구조를 분석해 새로운 코드를 만들어내는 기법”이다.
TypeScript는 타입 시스템 안에서 이를 구현할 수 있다.
즉, 타입 자체를 계산/분석/변환하는 타입 레벨 메타 프로그래밍이 가능하다.
InstanceType<T> — 생성자의 인스턴스 타입 추출InstanceType<T>는 클래스 생성자의 인스턴스 타입을 추출하는 유틸리티 타입이다.
class User {
id: number = 1;
name: string = "chan";
}
type U = InstanceType<typeof User>;
// U = User
typeof User → “클래스 생성자 함수의 타입”InstanceType<typeof User> → 생성자의 반환값(= 인스턴스 타입)type MyInstanceType<T extends new (...args: any[]) => any> =
T extends new (...args: any[]) => infer R ? R : never;
| 구문 | 의미 |
|---|---|
T extends new (...args: any[]) => any | 생성자 함수 타입만 받겠다는 제약 |
new (...args) => infer R | 생성자의 반환 타입(인스턴스)을 추론 |
R | 최종 추출된 인스턴스 타입 |
class Product {
constructor(public name: string, public price: number) {}
}
type ProductInstance = MyInstanceType<typeof Product>;
// ProductInstance = Product
ConstructorParameters<T> — 생성자 매개변수 타입 추출ConstructorParameters<T>는
클래스 생성자의 인자 타입들을 튜플 형태로 추출하는 타입이다.
class User {
constructor(public name: string, public age: number) {}
}
type Params = ConstructorParameters<typeof User>;
// Params = [string, number]
type MyConstructorParameters<T extends new (...args: any[]) => any> =
T extends new (...args: infer P) => any ? P : never;
| 구문 | 의미 |
|---|---|
new (...args: infer P) | 생성자 인자 목록을 P로 추론 |
P | 최종 매개변수 타입 튜플 |
never | 생성자 타입이 아닐 때 |
class Product {
constructor(public id: number, public name: string) {}
}
type ProductArgs = MyConstructorParameters<typeof Product>;
// [number, string]
TypeScript는 "함수"와 "클래스 생성자"를 각각 다른 타입으로 취급한다.
아래는 그 차이를 정리한 표다.
| 대상 | 내장 타입 | 추출 대상 | 예시 |
|---|---|---|---|
| 함수(Function) | Parameters<T> | 인자 타입 목록 | (x: number) => void → [number] |
| 함수(Function) | ReturnType<T> | 반환 타입 | () => string → string |
| 클래스(Class) | ConstructorParameters<T> | 생성자 인자 목록 | new (id: number) → [number] |
| 클래스(Class) | InstanceType<T> | 인스턴스 타입 | typeof User → User |
💡 즉,
ConstructorParameters와InstanceType은 “클래스 버전의 Parameters/ReturnType”이다.
function createInstance<T extends new (...args: any[]) => any>(
ctor: T,
...args: ConstructorParameters<T>
): InstanceType<T> {
return new ctor(...args);
}
class User {
constructor(public name: string, public age: number) {}
}
const user = createInstance(User, "chan", 29);
// user: User
ConstructorParameters<T>InstanceType<T>class ApiClient {
constructor(private baseUrl: string) {}
get(path: string) {
return `GET ${this.baseUrl}/${path}`;
}
}
function wrapClient<T extends new (...args: any[]) => any>(Client: T) {
return function (...args: ConstructorParameters<T>): InstanceType<T> {
const instance = new Client(...args);
console.log("클라이언트 생성됨:", instance);
return instance;
};
}
const createApi = wrapClient(ApiClient);
const api = createApi("https://example.com");
// api: ApiClient
api.get("posts");
// "GET https://example.com/posts"
React Hook Factory, Service Locator, API Wrapper 같은 패턴에서 자주 활용되는 구조이다.
추상 클래스도 ‘생성자 타입’으로 다룰 수 있다.
type AbstractConstructor<T = any> = abstract new (...args: any[]) => T;
abstract class BaseService {
abstract execute(): void;
}
class UserService extends BaseService {
execute() {
console.log("User Service 실행");
}
}
function runService<T extends AbstractConstructor<BaseService>>(Ctor: T) {
const instance = new Ctor();
instance.execute();
}
runService(UserService); // OK
InstanceType<T>와 ConstructorParameters<T>는
단순 유틸리티 타입이 아니라
“클래스의 타입 구조를 분석하고 재사용하는 메타 프로그래밍 기법의 핵심”이다.
이를 이해하면:
같은 구조를 모두 타입 레벨에서 안전하게 설계할 수 있다.
| 개념 | 역할 | 예시 |
|---|---|---|
Parameters<T> | 함수 인자 타입 추출 | (x: number) => void → [number] |
ReturnType<T> | 함수 반환 타입 추출 | () => string → string |
ConstructorParameters<T> | 생성자 인자 추출 | new (id: number) → [number] |
InstanceType<T> | 인스턴스 타입 추출 | typeof User → User |
💡 한 문장 요약
“함수와 클래스의 내부 타입 구조를 분석하고 자동으로 추출하는 것 — 그것이 TypeScript 메타 프로그래밍의 핵심이다.”