조건부 타입은 extends와 삼항 연산자(? :)를 활용해 타입을 상황에 따라 다르게 설정할 수 있게 해줌. 특히 제네릭과 함께 사용할 때 매우 강력하며, 값의 타입에 따라 반환 타입을 유연하게 조절할 수 있음. 타입스크립트의 타입 로직에 논리적 분기를 넣을 수 있다는 점이 핵심임.
type A = number extends string ? number : string;
// A → string
number가 string의 서브타입인지 여부에 따라 결과가 달라짐. 위 예시는 false가 되어 A는 string이 됨.
type ObjA = { a: number };
type ObjB = { a: number; b: number };
type B = ObjB extends ObjA ? number : string;
// B → number (ObjB는 ObjA의 슈퍼셋)
type StringNumberSwitch<T> = T extends number ? string : number;
let varA: StringNumberSwitch<number>; // string
let varB: StringNumberSwitch<string>; // number
타입 변수 T가 number면 결과는 string, 아니면 number가 됨.
function removeSpaces<T>(text: T): T extends string ? string : undefined {
if (typeof text === "string") {
return text.replaceAll(" ", "") as any;
} else {
return undefined as any;
}
}
removeSpaces("hello world") → stringremoveSpaces(undefined) → undefined하지만 위 방식은 as any 때문에 타입 안정성이 깨질 수 있음. 예를 들어 string이 아닌 값을 반환해도 타입 검사를 통과함.
function removeSpaces<T>(text: T): T extends string ? string : undefined;
function removeSpaces(text: any) {
if (typeof text === "string") {
return text.replaceAll(" ", "");
} else {
return undefined;
}
}
let result = removeSpaces("hi im winterlood"); // string
let result2 = removeSpaces(undefined); // undefined
오버로딩을 사용하면 조건부 타입의 결과를 실제로 타입스크립트가 추론할 수 있게 되므로, as any 없이도 타입 안정성을 확보할 수 있음.
if-else 같은 논리를 넣을 수 있게 해주는 기능임.A extends B ? C : D 형식으로 작성하며, 특히 제네릭과 함께 강력함.as any보다는 함수 오버로딩을 통한 추론이 더 안전하고 실용적임.