조건부 타입(Conditional Types)은 T extends U ? X : Y 꼴로, 타입 수준에서 if-else를 하는 문법이다. infer는 조건부 타입 안에서 타입의 일부를 변수로 붙잡아 꺼낼 때 쓴다.
type IsString<T> = T extends string ? true : false
type ElementType<T> = T extends (infer U)[] ? U : never
type Unwrap<T> = T extends Promise<infer U> ? U : T
async function fetchUser() { return { id: 'u1', name: 'Kim' } }
type User = Unwrap<ReturnType<typeof fetchUser>> // { id: string; name: string }- 함수 반환 타입에서 도메인 타입을 끌어오면 함수가 바뀔 때 타입이 같이 따라온다. 같은 타입을 두 번 선언하지 않는다
- 입력 타입에 따라 반환 타입을 바꾸는 API(Application Programming Interface)를 오버로드 없이 표현할 수 있다(함수 오버로드와 시그니처)
- 내장
ReturnType,Parameters,Awaited,Exclude가 모두 이 방식으로 만들어졌다(유틸리티 타입) - 과하면 독이다. 한 줄짜리 타입 퍼즐은 팀원이 읽지 못하고 에러 메시지도 난해해진다. 고급 타입의 목적은 똑똑해 보이는 것이 아니라 실수를 막고 변경을 안전하게 하는 것이다(KISS 원칙)
키를 돌며 바꾸는 쪽은 매핑된 타입과 keyof, 기초는 제네릭.