조건부 타입(conditional type)이란 extends
와 삼항 연산자를 이용해 조건에 따라 각각 다른 타입을 정의하도록 돕는 문법입니다.
type A = number extends string ? string : number; // A = number
type ObjA = {
a: number
};
type ObjB = {
a: number;
b: number;
};
type B = ObjB extends ObjA ? number : string; // B = number
type StringNumberSwitch<T> = T extends number ? string : number;
let varA: StringNumberSwitch<number>; // varA: string
let varB: stringNumberSwitch<string>; // varB: number
function removeSpace<T>(text: T): T extends string ? string : undefined;
function removeSpace(text: any) {
// any 타입 별칭이 컴파일 에러를 감지해주지 못하므로 위의 overload signature를 활용해야 합니다.
if (typeof text === "string") {
return text.replaceAll(" ", "");
} else {
return undefined;
}
}
let result = removeSpaces("hi im winterlood");
result.toUpperCase();
분산적인 조건부 타입(distributive conditional types)이란 조건부 타입(conditional types)이 유니온 타입(union types)에 적용될 때 각각의 조건부 타입이 유니온 타입의 각각의 멤버에 분산(distribute)되는 현상을 의미합니다.
// 조건부 타입
type StringNumberSwitch<T> = T extends number ? string : number;
let a: StringNumberSwitch<number>; // a: string
let b: StringNumberSwitch<string>; // b: number
// 분산적인 조건부 타입
let c: StringNumberSwitch<number | string>; // c: string | number
// StringNumberSwitch<number> |
// StringNumberSwitch<string>
let d: StringNumberSwitch<boolean | number | string>; // d: number | string
// 1 단계
// StringNumberSwitch<boolean> |
// StringNumberSwitch<number> |
// StringNumberSwitch<string>
// 2 단계
// number |
// string |
// number
type Exclude<T, U> = T extends U ? never : T;
type A = Exclude<number | string | boolean, string>; // A = number | boolean
// 1 단계
// Exclude<number, string> |
// Exclude<string, string> |
// Exclude<boolean, string>
// 2 단계
// number |
// never |
// boolean
type Extract<T, U> = T extends U ? T : never;
type B = Extract<number | string | boolean, string>; // B = string
// 1 단계
// Extract<number, string>
// Extract<string, string> |
// Extract<boolean, string>
// 2 단계
// never |
// string |
// never
// 분산적인 조건부 타입을 방지하려면 튜플 타입을 사용하면 됩니다.
type StringNumberSwitch<T> = [T] extends [number] ? string : number;
infer
키워드는 타입스크립트의 조건부 타입에서 사용되며, 해당 조건이 참이 될 때 추론할 수 있는 타입을 나타냅니다.
type ReturnType<T> = T extends () => infer R ? R : never;
/**
* 조건식 T extends () => infer R에서 infer R은
* 이 조건식을 참이 되도록 만들 수 있는 최적의 R 타입을 추론하라는 의미입니다.
*/
type FuncA = () => string;
type FuncB = () => number;
type A = ReturnType<FuncA>; // A = string
type B = ReturnType<FuncB>; // B = number
type C = ReturnType<number>; // C = never
type PromiseUnpack<T> = T extends Promise<infer R> ? R : never;
type PromiseA = PromiseUnpack<Promise<number>>; // PromiseA = number
type PromiseB = PromiseUnpack<Promise<string>>; // PromiseB = string
Reference