
복잡한 제네릭 타입들을 중첩해서 쓰다 보면 결과 타입이 뭔지 도저히 알 수가 없다.
Omit<Pick<SomeType, 'a' | 'b'> & OtherType, 'c'>
IDE에서 보면 이런 식으로 나와서 매번 최종 타입까지 찾아가서 봐야한다.
그러던 중에 유튜브 영상에서 이런 헬퍼 타입을 만들어서 사용하는 방법을 보게 되었다.
/**
* @description 복잡한 교차/중첩 타입을 펼쳐서 읽기 쉽게 표시하는 헬퍼 타입
* 타입의 의미는 그대로 유지하면서, IDE에서 보기 좋은 형태로 변환한다.
*/
export type Prettify<T> = {
[K in keyof T]: T[K]
} & {}
// Before: 복잡한 타입
type Complex = Omit<{ a: string; b: number; c: boolean }, 'c'> & { d: Date }
// IDE에서 "Omit<{...}, 'c'> & { d: Date }" 로 표시됨
// After: Prettify 적용
type Clean = Prettify<Complex>
// IDE에서 "{ a: string; b: number; d: Date }" 로 깔끔하게 표시됨
// 유니온 타입도 잘 동작함
type PolicyCategories = Prettify<NonNullable<PoliciesSearch['category']> | '전체'>
// IDE에서 "일자리" | "주거" | "금융" | "교육" | "전체" 로 깔끔하게 표시됨
타입을 "예쁘게" 만들어주는 마법 같은 느낌이어서 원리를 찾아봤다.
{ [K in keyof T]: T[K] }
이 부분이 TypeScript에게 "T의 모든 키를 순회하면서 새로운 객체 타입을 만들어라"라고 지시한다. 교차 타입이나 복잡한 조건부 타입을 단일 객체 리터럴로 재구성하는 역할이다.
& {}
이게 핵심이다. 빈 교차 타입을 추가하면:
TypeScript 컴파일러가 타입을 즉시 평가하도록 강제함
지연 계산을 방지하고 타입 캐시를 무효화함
내부 표현을 사용자가 읽기 쉬운 형태로 정규화함
AI의 답변은 다음과 같다.
TypeScript는 성능을 위해 복잡한 타입 계산을 가능한 한 미루려고 한다. 그래서 IDE에서 보면
Omit<Pick<...>, '...'> & {...}같은 내부 표현 그대로 보여준다.
Prettify는 이런 지연 계산을 강제로 실행시켜서 최종 결과를 보여주는 방식이다.
여러 제네릭 타입을 조합해서 만든 복잡한 타입
유니온 타입이 복잡하게 중첩된 경우
라이브러리 타입들을 조합해서 만든 커스텀 타입
동료들이 타입을 이해하기 쉽게 만들고 싶을 때
타입을 볼 때 타입 선언부까지 가지 않고 확인할 수 있어서 좋은 것 같다. 이렇게 타입 계산을 강제로 시키는 방식이 IDE에서 성능을 실제로 떨어뜨리는지는 확인해볼 필요가 있다.