TypeScript 조건부 타입(Conditional Types)은 T extends U ? X : Y 형태로 타입 수준의 조건 분기를 표현한다. 타입 추론과 결합하면 강력한 타입 변환 패턴을 만들 수 있다.
기본 문법
typescript
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
type C = IsString<'hello'>; // true (리터럴도 string에 할당 가능)
분산 조건부 타입
typescript
// 유니온 타입에 조건부 타입 적용 시 각 멤버에 분산
type ToArray<T> = T extends any ? T[] : never;
type StrOrNum = ToArray<string | number>;
// string[] | number[] (string[]|number[] 아님)
// 분산 막기: 튜플로 감싸기
type NoDistribute<T> = [T] extends [any] ? T[] : never;
type D = NoDistribute<string | number>;
// (string | number)[]
infer 키워드
typescript
// 조건 검사 중 타입 추론
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type A = UnpackPromise<Promise<string>>; // string
type B = UnpackPromise<number>; // number
// 함수 반환 타입 추출
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function greet(name: string): string { return `Hello ${name}`; }
type GreetReturn = MyReturnType<typeof greet>; // string
// 튜플의 첫 번째 요소
type Head<T> = T extends [infer H, ...any[]] ? H : never;
type First = Head<[number, string, boolean]>; // number
실용 패턴
typescript
// 깊은 Readonly
type DeepReadonly<T> = T extends (infer U)[]
? ReadonlyArray<DeepReadonly<U>>
: T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T;
// 함수 오버로드 타입 선택
type StringOrNumber<T extends string | number> =
T extends string ? string : number;
// 키 필터링
type FunctionKeys<T> = {
[K in keyof T]: T[K] extends Function ? K : never
}[keyof T];
관련 개념