TypeScript 5.8 Conditional Type 추론 개선으로 타입 가드 리팩토링

문제 상황

프로젝트에서 API 응답 타입을 처리하는 유틸리티 함수를 작성하던 중, 복잡한 conditional type에서 타입 추론이 제대로 되지 않는 문제를 겪었다.

type ApiResponse<T> = 
  | { status: 'success'; data: T }
  | { status: 'error'; error: string };

function processResponse<T>(response: ApiResponse<T>) {
  if (response.status === 'success') {
    // TS 5.7까지는 여기서 response.data 타입이 제대로 좁혀지지 않았음
    return response.data;
  }
  throw new Error(response.error);
}

TypeScript 5.8 개선사항

5.8 버전으로 업데이트 후, 유니온 타입의 discriminated union 추론이 크게 개선되었다. 특히 제네릭과 결합된 경우에도 타입 가드가 정확히 작동한다.

type Result<T, E = Error> = 
  | { ok: true; value: T }
  | { ok: false; error: E };

function unwrap<T, E>(result: Result<T, E>): T {
  if (!result.ok) {
    throw result.error; // error 타입이 정확히 E로 추론됨
  }
  return result.value; // value 타입이 T로 정확히 추론됨
}

실무 적용

이 개선사항 덕분에 기존에 as 단언을 사용하던 코드를 제거할 수 있었다. 특히 폼 유효성 검사 로직에서 타입 안정성이 크게 향상되었다.

type ValidationResult<T> = 
  | { valid: true; data: T }
  | { valid: false; errors: Record<keyof T, string[]> };

function handleSubmit<T>(result: ValidationResult<T>) {
  if (result.valid) {
    // 5.8부터는 result.data를 안전하게 사용 가능
    sendToApi(result.data);
  } else {
    // errors도 정확한 타입으로 추론
    displayErrors(result.errors);
  }
}

컴파일러 성능도 체감상 개선되어, 대규모 모노레포에서도 타입 체크 시간이 약 15% 단축되었다.