[ map 타입 ]
✏️ map 타입을 사용하면 기존에 있는 타입을 이용하면서 다른 형태로 변환이 가능하다.
예시 코드와 map 타입에 대해 살펴보기 전 아래의 사항을 짚고 넘어가면 좋을 것 같다.
- 배열의 map API의 개념으로 접근하자.
- for ... in 반복문의 개념으로 접근하자.
- keyof : 모든 키를 가져온다.
- in : 들어있다.
[ 예시 코드 1]
type Animal = {
name: string;
age: number;
};
타입 Animal을 만들었으며, 내부에는 속성 name, age에 대한 타입이 명시되어 있다.
type AnimalOptional = {
name?: string;
age?: number;
};
타입 AnimalOprtional을 만들었으며, 내부에는 속성 name, age에 대한 타입이 명시되어 있고 해당 속성은 있을 수 있고 없을 수 있다.
타입 Animal을 만들었지만, 해당 속성을 옵셔널로 사용해야 하는 상황이 발생하여 추가로 AnimalOprtional을 만들어 주었다.
type AnimalReadonly = {
readonly name: string;
readonly age: number;
};
타입 AnimalReadonly을 만들었으며, 내부에는 속성 name, age에 대한 타입이 명시되어 있고 해당 속성은 값을 변경할 수 없는 읽기 전용 속성이다.
작업 도중 해당 속성의 값을 변경하지 못하도록 readonly를 추가해야 하는 상황이 발생하여 또다시 추가로 AnimalReadonly를 만들어 주었다.
위의 방식은 단점이 너무나 명확하다. 코드를 작성하다가 타입을 만들어 주어야 하며, 결국에는 속성에 대한 타입 명시가 중복으로 사용되었다. 이 경우 map 타입을 사용하면 해당 문제를 해결할 수 있다.
[ map 타입 적용 ]
🔥 map 타입을 살펴보기 전에 한 번 더 살펴보고!
type Animal = {
name: string;
age: number;
};
type Optional<T> = {
[P in keyof T]?: T[P];
};
type AnimalOptional = Optional<Animal>
타입 Optional 제네릭 T에 Animal 타입을 전달하고 있다.
[P in keyof T]을 분리해서 살펴보자.
- keyof T : T에 있는 모든 키 중에
- P in : 들어있는 것 중 하나인 P이다.
이때 [P in keyof T]는 for ... in 반복문처럼 작동한다. 즉, P에 T에 있는 속성의 모든 값을 넣는다.
type Optional<Animal> = {
[P in keyof Animal]?: T[P];
};
// === 아래와 같은 동작
type Optional<Animal> = { // Animal의 키에는 name, age가 있다.
// [P in keyof Animal]?: T[P];
name: string; // ➡️ 첫 번째로 만난 키 name을 P에 전달, Animal["name"]은 sting
};
type Optional<Animal> = { // Animal의 키에는 name, age가 있다.
// [P in keyof Animal]?: T[P];
name?: string;
age?: number; // ➡️ 두 번째로 만난 키 age를 P에 전달, Animal["age"]은 name
};
동작은 위와 같이 동작을 한다. "모든 키를 순회하면서 가져온다!"로 접근하면 좋을 것 같다.
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type AnimalReadonly = Readonly<Animal>
옵셔널 말고 readonly도 적용이 가능하다. T로 전달받은 타입의 모든 속성을 순회하면서 readonly를 추가하게 된다.
✏️타입 Optional, Readonly은 각각 전달받은 타입을 순회하여 옵셔널 처리와 readonly 처리를 하게 되어 재사용성이 굉장히 높아졌다.
❗️단 제네릭 T에 전달받은 타입에 없는 속성은 사용할 수 없다.
[ 예시 코드 2]
type Animal = {
name: string;
age: number;
};
type Nullable<T> = { [P in keyof T]: T[P] | null }; // value의 타입이 T[P]또는 null 가능
const obj: Nullable<Animal> = {
name: "",
age: null,
};
타입 Nullable은 map 타입으로 속성의 값의 타입이 T[P]또는 null 가능하다.
obj는 타입 Nullable으로 명시되어 있다. map 타입으로 T로 전달받은 타입의 속성을 순회하고, 순회하는 각각의 속성 name, age의 값의 타입을 T[P] | null 로 명시해주고 있다.
이는 위와 같이 각각의 속성 name, age의 값의 타입이 T에 명시된 string과 number가 될 수도 있고 null이 될 수 있다.
[ 예시 코드 3]
type Proxy<T> = {
get(): T;
set(value: T): void;
};
type Proxify<T> = {
// ⭐️ 빙글 빙글 돌면서 가져온 value의 타입을 타입 Proxy에 넣어줌
[P in keyof T]: Proxy<T[P]>;
};
타입 Proxify는 map 타입으로 명시되어 있으며, 속성의 값 타입을 타입 Proxy으로 한 번 더 감싸주고 있다.
타입 Proxify의 제네릭 T에 전달되는 타입의 속성을 순회하면서 각각의 속성의 타입을 타입 Proxy에 전달하는 것이다.
[ condition 타입 ]
✏️ condtion 타입은 말 그대로 타입을 컨디션에 따라 명시가 가능하다.
type Check<T> = T extends string ? boolean : number;
const check = Check<"qq"> // 변수 check는 string 타입!
타입 Check 제네릭 T에 전달받는 타입이 sting 타입을 확장한다면, boolean 타입을 명시하고 아니면 number 타입을 명시하도록 삼항 연산자를 사용하여 타입을 명시해주고 있다.
✏️ 이 경우 제네릭 T는 타입 string을 확장할 경우 모두 string 타입으로 취급받는다. 즉 제네릭 T에 타입이 아니라 문자열을 전달할 경우도 string 타입을 전달한 것으로 취급한다.
[ 유틸리티 타입 ]
😃 위의 readonly 속성을 붙이고 옵셔널을 붙이는 타입은 이미 유틸리티 타입으로 만들어져 있기 때문에 유틸리티 타입을 사용하면 된다! 다른 유틸리티 타입도 살펴봐야겠다.
🎉 이렇게 map타입과 condition타입을 사용하면 기존의 타입을 보장하고 재사용하면서 조금 다른 타입을 만들 수 있다.
'TypeScript' 카테고리의 다른 글
Promise에 제네릭(Generic) 문법 적용 (0) | 2023.01.06 |
---|---|
union 타입과 intersection 타입 (0) | 2023.01.06 |
인터페이스와 클래스 (0) | 2023.01.05 |
열거형 타입 (Enum) (0) | 2023.01.04 |
타입 스크립트 구성 옵션 (0) | 2023.01.03 |