이 글은 Chat GPT로 TypeScript를 공부하며 정리한 글입니다.
앞선 글에서는 InstanceType<T>, ConstructorParameters<T>를 활용해
클래스의 타입 구조를 추론하는 메타 프로그래밍 패턴을 다뤘다.
이번 글에서는 그 개념을 API 응답 타입 분석으로 확장해
Promise나 AxiosResponse처럼 중첩된 구조에서 “실제 데이터 타입만 추출”하는 패턴을 다룬다.
interface ApiResponse<T> {
status: number;
data: {
result: T;
message: string;
};
}
type User = { id: number; name: string };
type Response = Promise<ApiResponse<User>>;
Response의 실제 데이터는 User,
하지만 타입은 Promise<ApiResponse<User>>로 감싸져 있다.
💡 이럴 때
User만 추출하고 싶을 때가 많다.
→ 예: React QueryuseQuery의data타입 정의.
먼저 Promise<T>에서 T를 추출하는 UnwrapPromise를 만든다.
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type A = UnwrapPromise<Promise<string>>; // string
type B = UnwrapPromise<number>; // number
API 응답 객체 내부 구조가 ApiResponse<T>라면
T를 자동 추출하도록 만든다.
type UnwrapApiResponse<T> =
T extends { data: { result: infer R } } ? R : T;
type A = UnwrapApiResponse<ApiResponse<{ id: number }>>;
// { id: number }
실무에서는 둘이 함께 쓰인다
type Response = Promise<ApiResponse<{ id: number; name: string }>>;
이 경우 UnwrapPromise → UnwrapApiResponse 순서로 재귀적으로 처리해야 한다.
type ExtractApiData<T> =
T extends Promise<infer U> ? ExtractApiData<U> :
T extends { data: { result: infer R } } ? R :
T;
결과
type FinalData = ExtractApiData<Response>;
// { id: number; name: string }
✅ Promise가 몇 번 중첩되어 있어도 자동으로 최종 데이터 타입을 반환한다.
Axios를 쓴다면 구조가 약간 다르다
import { AxiosResponse } from "axios";
type Response = Promise<AxiosResponse<{ user: { id: number; name: string } }>>;
이 경우 AxiosResponse의 구조를 타고 들어가야 한다.
type ExtractAxiosData<T> =
T extends Promise<infer U> ? ExtractAxiosData<U> :
T extends AxiosResponse<infer D> ? D :
T;
결과
type A = ExtractAxiosData<Response>;
// { user: { id: number; name: string } }
이번엔 “result 안에 또 data가 있는” 구조도 처리해보자
type DeepResponse = Promise<{
data: {
result: {
user: { id: number; name: string };
};
};
}>;
이를 재귀적으로 탐색하려면 다음과 같이 정의한다
type DeepExtract<T> =
T extends Promise<infer U> ? DeepExtract<U> :
T extends { data: infer D } ? DeepExtract<D> :
T extends { result: infer R } ? DeepExtract<R> :
T;
결과
type Final = DeepExtract<DeepResponse>;
// { user: { id: number; name: string } }
✅ Promise → data → result 계층을 모두 순회하며 최종 데이터만 남김.
async function fetchUser() {
return {
data: {
result: { id: 1, name: "chan" }
}
};
}
type FetchReturn = ReturnType<typeof fetchUser>; // Promise<...>
type UserData = DeepExtract<FetchReturn>; // { id: number; name: string }
React Query의 useQuery<UserData>(...)와 완벽히 호환된다.
이제 Promise, AxiosResponse, ApiResponse 등
중첩된 구조를 모두 통과해 최종 데이터 타입만 추출할 수 있다.
| 이름 | 설명 | 예시 |
|---|---|---|
UnwrapPromise<T> | Promise 내부 타입 추출 | Promise<string> → string |
UnwrapApiResponse<T> | ApiResponse 내부 result 추출 | { data: { result: T } } → T |
DeepExtract<T> | 중첩 구조를 재귀적으로 탐색 | Promise<ApiResponse<T>> → T |
💡 한 문장 요약
“타입스크립트는 중첩된 구조도 계산할 수 있다 — 타입은 결국 트리(tree)다.”