TypeScript Generic 타입 추론 문제 해결

문제 상황

재택근무를 하면서 여러 API 호출 로직을 리팩토링하는 작업을 진행했다. 공통으로 사용할 API 응답 래퍼 함수를 만들었는데, Generic 타입 추론이 예상대로 동작하지 않았다.

interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

const fetchData = async <T>(url: string): Promise<ApiResponse<T>> => {
  const response = await fetch(url);
  return response.json();
};

// 사용 시 타입 추론 실패
const result = await fetchData('/api/users'); // ApiResponse<unknown>

타입을 명시적으로 전달하지 않으면 unknown으로 추론되어 매번 제네릭을 넘겨야 했다.

해결 방법

결국 타입 가드와 함께 사용하는 방식으로 변경했다.

interface User {
  id: number;
  name: string;
}

const isUser = (data: any): data is User => {
  return typeof data.id === 'number' && typeof data.name === 'string';
};

const fetchUser = async (url: string) => {
  const response = await fetchData<User>(url);
  if (!isUser(response.data)) {
    throw new Error('Invalid user data');
  }
  return response;
};

각 도메인별로 fetch 함수를 분리하니 타입 안정성도 높아지고 재사용성도 좋아졌다.

교훈

TypeScript의 타입 추론은 강력하지만 런타임 데이터에 대해서는 한계가 있다. API 응답처럼 외부에서 들어오는 데이터는 타입 가드로 검증하는 것이 안전하다. 무리하게 Generic으로 모든 것을 해결하려 하지 말고, 적절한 추상화 수준을 찾는 것이 중요하다.

TypeScript Generic 타입 추론 문제 해결