TypeScript 5.6 제네릭 제약 조건 개선과 실무 적용

문제 상황

사내 API 클라이언트 라이브러리를 리팩토링하던 중, 엔드포인트별로 요청/응답 타입을 자동으로 매핑하는 제네릭 함수에서 타입 추론이 제대로 동작하지 않는 문제가 있었다.

interface EndpointMap {
  '/users': { req: { id: string }, res: User[] }
  '/posts': { req: { limit: number }, res: Post[] }
}

function apiCall<T extends keyof EndpointMap>(
  endpoint: T,
  params: EndpointMap[T]['req']
): Promise<EndpointMap[T]['res']> {
  // 구현
}

기존 코드에서는 params 타입이 복잡한 조건부 타입과 결합될 때 any로 fallback되는 경우가 많았다.

TypeScript 5.6 적용

5.6 버전에서 개선된 제네릭 제약 조건 추론을 활용하니 중첩된 제네릭에서도 타입이 정확히 좁혀졌다.

type InferResponse<T extends keyof EndpointMap> = 
  EndpointMap[T]['res'] extends Array<infer U> ? U : never;

function getFirstItem<T extends keyof EndpointMap>(
  endpoint: T,
  params: EndpointMap[T]['req']
): Promise<InferResponse<T> | undefined> {
  return apiCall(endpoint, params).then(res => res[0]);
}

// 타입 추론 정상 동작
const user = await getFirstItem('/users', { id: '123' }); // User

개선 효과

  • 복잡한 유틸리티 타입에서 as 키워드 사용 빈도 50% 감소
  • IDE 자동완성 정확도 향상으로 생산성 개선
  • 런타임 에러 가능성이 있던 6건의 타입 불일치를 컴파일 단계에서 검출

다만 빌드 시간이 약 8% 증가했는데, 타입 체크 정확도를 고려하면 충분히 감수할 만한 수준이었다.