반응형
조건부 타입
조건부 타입은 extends 와 삼항 연산자를 이용해 조건에 따라 각각 다른 타입을 정의하도록 돕는 문법이다.
type A = number extends string ? number : string;
- 위 조건부 타입의 조건식 number extends string은 number 타입이 string 타입의 서브타입이 아니기 때문에 거짓이 되고 그 결과 타입 A는 string 타입이 된다.
type ObjA = {
a: number;
};
type ObjB = {
a: number;
b: number;
};
type B = ObjB extends ObjA ? number : string;
- B는 ObjB는 ObjA의 서브 타입 이므로 조건식이 참이되어 number 타입이 된다.
제네릭 조건부 타입
조건부 타입은 제네릭과 함께 사용할 대 그 위력이 극대화 된다.
type StringNumberSwitch<T> = T extends number ? string : number;
let varA: StringNumberSwitch<number>;
// string
let varB: StringNumberSwitch<string>;
// number
- varA는 T에 number 타입을 할당한다. 그 결과 조건식이 참이 되어 string 타입이 된다.
- varB는 T에 string 타입을 할당한다. 그 결과 조건식이 거짓이 되어 number 타입이 된다.
이번에는 실용적인 예제를 살펴본다. 다음은 매개변수로 String 타입의 값을 제공받아 공백을 제거한 다음 반환하는 함수이다.
function removeSpaces(text: string) {
return text.replaceAll(" ", "");
}
let result = removeSpaces("hi im winterlood");
- 이때 이 removeSpaces 함수의 매개변수에 undefined이나 null 타입의 값들도 제공될 수 있다고 가정한다.
그럼 매개변수의 타입을 다음과 같이 수정해야 한다.
function removeSpaces(text: string | undefined | null) {
return text.replaceAll(" ", ""); // ❌ text가 string이 아닐 수 있음
}
let result = removeSpaces("hi im winterlood");
- 이때 함수 내부에서 text의 타입은 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
infer
infer 는 조건부 타입 내에서 특정 타입을 추론하는 문법이다.
- infer는 다음과 같이 특정 함수 타입에서 반환값의 타입만 추출하는 특수한 조건부 타입인 ReturnType을 만들 때 이용할 수 있다.
type ReturnType<T> = T extends () => infer R ? R : never;
type FuncA = () => string;
type FuncB = () => number;
type A = ReturnType<FuncA>;
// string
type B = ReturnType<FuncB>;
// number
조건식 T extends () => infer R에서 infer R은 이 조건식을 참이 되도록 만들 수 있는 최적의 R 타입을 추론하라는 의미이다.
따라서 A 타입을 계산할 때의 위 코드의 흐름은 다음과 같다.
- 타입 변수 T에 함수 타입 FuncA가 할당된다.
- T는 () ⇒ string 이 된다.
- 조건부 타입의 조건식은 다음 형태가 된다 () ⇒ string extends () ⇒ infer R ? R : never
- 조건식을 참으로 만드는 R 타입을 추론 한다 그 결과 R은 string이 된다.
- 추론이 가능하면 이 조건식을 참으로 판단한다 따라서 결과는 string이 된다.
만약 다음과 같이 추론이 불가능하다면 조건식을 거짓으로 판단한다.
type ReturnType<T> = T extends () => infer R ? R : never;
type FuncA = () => string;
type FuncB = () => number;
type A = ReturnType<FuncA>;
// string
type B = ReturnType<FuncB>;
// number
type C = ReturnType<number>;
// 조건식을 만족하는 R추론 불가능
// never
- romise의 resolve 타입을 infer를 이용해 추출하는 예 이다.
type PromiseUnpack<T> = T extends Promise<infer R> ? R : never;
// 1. T는 프로미스 타입이어야 한다.
// 2. 프로미스 타입의 결과값 타입을 반환해야 한다.
type PromiseA = PromiseUnpack<Promise<number>>;
// number
type PromiseB = PromiseUnpack<Promise<string>>;
// string
반응형
'Typescript' 카테고리의 다른 글
Typescript | 타입조작 ( Indexed Access Type, key of & typeof 연산자, mapped Type, Template literal Type ) (3) | 2024.11.04 |
---|---|
Typescript | 제네릭 인터페이스, 제네릭 타입 별칭, 제네릭 클래스, Promise와 제네릭 (1) | 2024.11.03 |
Typescript | 제네릭, 타입 변수 응용, map, forEach 메서드 타입 정의 (1) | 2024.11.03 |
Typescript | 클래스, 접근 제어자, 인터페이스와 클래스 (1) | 2024.11.02 |
Typescript | 인터페이스, 확장, 선언 합치기 (0) | 2024.11.02 |