조건부 타입(conditional type)은 자바스크립트의 삼항연산자(condition ? trueExpression : falseExpression
)와 비슷한 형태를 가집니다.
SomeType extends OtherType ? TrueType : FalseType
extends
키워드의 왼쪽에 있는 타입(SomeType
)을 오른쪽에 있는 타입(OtherType
)에 할당할 수 있다면 TrueType
이 되고 그렇지 않다면 FalseType
이 됩니다.
// 제네릭 T가 string이라면 string array, 아니면 number array 타입
type IsStringType<T> = T extends string ? string[] : number[];
type StringType = IsStringType<string>; // type StringType = string[]
type NumberType = IsStringType<number>; // type NumberType = number[]
조건부 타입으로 제네릭의 타입을 제한할 수 있습니다.
// 제네릭 T를 boolean 타입으로 제한
// T가 true 라면 data는 string 타입, false라면 number 타입 지정
interface IsStringData<T extends boolean> {
data: T extends true ? string : null;
isString: T;
}
const stringData: IsStringData<true> = {
data: '문자열', // data 타입은 string
isString: true,
}
const nullData: IsStringData<false> = {
data: null, // data 타입은 null
isString: false,
}
조건부 타입의 조건식이 참으로 평가될 때 infer
키워드를 사용하여 타입을 추론할 수 있습니다.
따라서, infer
키워드는 조건부 타입의 extends
절에서만 사용할 수 있습니다.
infer
타입의 기본 구조는 다음과 같습니다.
// U가 추론할 수 있는 타입이면 X 타입, 아니면 Y 타입
T extends infer U ? X : Y
infer
키워드의 원리는 런타임에서 타입을 추론할 수 있도록 하고, 추론한 타입을 infer
타입 파라미터 U
에 할당합니다. 그리고 조건부 타입에 의해 조건식이 참이라면 파라미터(U
)를 아니라면 무시하는 것이 기본 동작입니다.
// 제네릭 T의 타입을 받아와서 R이라는 변수에 할당
type TestType<T> = T extends infer R ? R : null;
// test 타입은 string으로 추론
let test: TestType<string>;
type ArrayType<T> = T extends (infer R)[] ? R : unknown;
// stringType 타입은 string으로 추론
type stringType = ArrayType<string[]>;
// unknownType 타입은 unknown으로 추론
type unknownType = ArrayType<string>;
조금 더 복잡한 예시로는 ReturnType
이 있습니다.
ReturnType
은 infer
키워드를 사용하여 유틸리티 타입으로 만들어졌습니다.
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
infer
키워드가 어떻게 ReturnType
을 유틸리티 타입으로 만들었을까요?
먼저, 타입을 직접 정의하여 ReturnType을 작성해봅시다.
// ReturnType의 타입을 string 타입으로 명시
type ReturnType<T extends (...args: any) => any> = string;
const testFunction = (param: number) => {
return param.toString();
}
// test 타입은 string
const test: ReturnType<typeof testFunction> = 'String';
ReturnType<T>
의 타입이 string
이므로 변수 test
의 값으로 string
타입인 'String' 문자열을 할당할 수 있습니다.
함수 testFunction
의 반환값이 toString
에 의해 string
타입이 되었으므로 문제가 없지만 만약 testFunction
의 반환값이 string
이 아닌 다른 타입이라면 위 예시에 ReturnType<T>
의 타입을 string
타입으로 직접 명시한 것처럼 각 타입에 맞게 수정을 해야합니다.
또는 다음과 같이 유니온 타입으로 명시해줘야 합니다.
// 유니온 타입을 이용하여 ReturnType의 타입을 string 또는 number 타입으로 명시
type ReturnType<T extends (...args: any) => any> = string | number;
효율적으로 타입을 명시하기 위해 infer
키워드를 이용하여 타입을 직접 정의하는 것이 아니라 타입을 추론 시킵니다.
따라서, 각 타입에 따라 정의하거나 수정할 필요없이 추론에 의해 함수의 반환 타입에 의한 타입을 만들 수 있습니다.
참고