티스토리 뷰
🐶 조건부 타입
extends와 삼항 연산자를 이용해 조건에 따라 각각 다른 타입을 결정하는 문법
type A = number extends string? string : number;
// numebr는 string를 확장하는 타입(서브타입)이 아님 -> number 타입으로 정의
- number extends string?
- 조건이 참이라면 string 타입으로 설정
- 조건이 거짓이라면 number 타입으로 설정
number 타입은 string 타입의 서브 타입이 아니므로 타입 A는 number타입이 된다.
type ObjA = {
a: number;
}
type ObjB = {
a: number;
b: number;
}
type B = ObjB extends ObjA? number : string;
// objB는 objA를 확장한 타입 -> number 타입으로 정의
객체 타입도 마찬가지로 ObjB는 ObjA 타입의 서브타입이므로 조건식에 따라 타입 B가 결정된다.
- ObjB extends ObjA?
- 조건이 참이라면 number 타입으로 결정
- 조건이 거짓이라면 string 타입으로 결정
🐶 제네릭 조건부 타입
조건부 타입은 기본 타입과 사용할 때보다 제네릭과 함께 쓸 때 더 활용도가 높다.
type StringNumberSwitch<T> = T extends number? string : number;
let varA: StringNumberSwitch<number>; // number 타입으로 정의
let varB: StringNumberSwitch<string>; // string 타입으로 정의
타입 변수에 Number 타입이 할당되면 String 타입을 반환하고 그렇지 않다면 Number 타입을 반환하는 조건부 타입이다.
- varA는 T에 number 타입을 할당 -> 조건식이 참이 되므로 string 타입
- varB는 T에 string 타입을 할당 -> 조건식이 거짓이 되므로 number 타입
function removeSpaces(text: string) {
return text.replaceAll(' ', '');
}
let result = removeSpaces('hi im kiji');
매개변수로 String 타입의 값을 받아 공백을 제거해 반환하는 함수 removeSpaces를 구현했다.
이때 이 함수의 매개변수에 undefined나 null 타입의 값들도 제공될 수 있다고 가정하고 코드를 수정한다.
function removeSpaces(text: string | undefined | null) {
return text.replaceAll(' ', ''); // ❌ test가 string이 아닐 수 있음
}
let result = removeSpaces('hi im kiji');
함수 내부에서 text 타입은 String이 아니라 undefined나 null이 될 수도 있기 때문에 오류가 발생한다.
이러한 경우 타입을 좁혀 사용한다.
function removeSpaces(text: string | undefined | null) {
if (typeof text === 'string') {
return text.replaceAll(' ', '');
} else {
return undefined;
}
}
let result = removeSpaces('hi im kiji');
// string | undefined
하지만 이 경우 변수 result의 타입이 string | undefiend 타입으로 추론된다.
이를 해결하기 위해 타입 변수를 추가한 후 조건부 타입을 이용해 인수로 전달된 값의 타입이 String이면 반환값 타입도 String이고 아닌 경우 반환값 타입을 undefined로 만든다.
function removeSpaces<T>(text: T): T extends string? string : undefined{
if(typeof text === 'string'){
return text.replaceAll(' ','') as any;
// 조건부 타입의 결과를 내부에서 알 수 없으므로 오류 발생
// -> 타입 단언을 이용
} else{
return undefined as any;
// 조건부 타입의 결과를 내부에서 알 수 없으므로 오류 발생
// -> 타입 단언을 이용
}
}
let result = removeSpaces('hi im kiji');
result.toUpperCase();
let result2 = removeSpaces(undefined);
함수 내부에서는 조건부 타입의 결과를 알 수 없어 return문에서 오류가 발생하기 때문에 타입 단언을 이용해 반환값 타입을 any 타입으로 단언한다.
- result는 인수로 String 타입을 전달 -> 반환값의 타입은 String
- result2는 인수로 undefined를 전달 -> 반환값의 타입은 undefiend
하지만 any로 단언하는 것은 좋지 못한다. 첫 번째 return문에서 string이 아닌 타입의 값을 반환해도 오류를 감지하지 못하기 때문이다.
따라서 타입 단언보다는 함수 오버로딩을 이용한다.
// 오버로드 시그니쳐 사용하기
// 구현 시그니쳐 내부에서 추론이 가능함
function removeSpaces<T>(text: T): T extends string? string : undefined;
function removeSpaces(text: any) {
if(typeof text === 'string'){
return text.replaceAll(' ','');
// return text.replaceAll(' ','')as any;
// 조건부 타입의 결과를 함수 내부에서 알 수 없음 -> 타입 단언 이용하기
// 하지만 as any의 사용은 좋지 않음 -> 함수 오버로딩 구현하기
} else{
return undefined;
}
}
let result = removeSpaces('hi im kiji');
result.toUpperCase();
let result2 = removeSpaces(undefined);
오버로드 시그니쳐의 조건부 타입은 구현 시그니쳐 내부에서 추론이 가능하기 때문에 오버로드 시그니쳐를 추가해 함수 오버로딩을 구현한다.
'코딩 > 한 입 크기로 잘라먹는 타입스크립트' 카테고리의 다른 글
[TypeScript] infer (0) | 2023.09.25 |
---|---|
[TypeScript] 분산적인 조건부 타입 (0) | 2023.09.25 |
[TypeScript] 타입 조작 - 템플릿 리터럴 타입 (0) | 2023.09.23 |
[TypeScript] 타입 조작 - 맵드 타입 (0) | 2023.09.22 |
[TypeScript] 타입 조작 - keyof 연산자 (0) | 2023.09.22 |