2024년 3월 9일
이번 점심 뭐먹지 미션은 자바스크립트가 아닌 타입스크립트를 사용하는 방식이었다.
export interface Irestaurant {
category: Icategory;
name: string;
distance: Idistance;
description?: string;
link?: string;
}
카테고리, 음식점이름, 거리는 필수 값이지만, 설명과 링크는 undefined를 허용해준다.
이 음식점 관련 된 값들을 RestaurantRestaurantStateStore라는 클래스에서 전역으로 관리해주도록 했다.
하지만 문제는 초기에 해당 state의 값은 입력을 받기 전이어서 모두 undefined를 보장해주어야 했는데 처음에는 IrestaurantField 라는 undefined를 허용해주는 타입을 만들어서 대응해주었다.
export interface IrestaurantField {
category?: Icategory;
name?: string;
distance?: Idistance;
description?: string;
link?: string;
}
만들면서도 불만족스러운 부분이었다.
해당 속성에 전부 undefined를 허용해주기 위해서 새로운 타입을 지정한다는것 자체가 비용이 큰 행위라고 생각했다.
감사하게도 바로 다음날 Mapped Type에 대해서 공부하게 됐고, 이를 적용했다.
type RestaurantField = {
[K in keyof Irestaurant]?: Irestaurant[K];
};
const restaurantField: RestaurantField = {
category: undefined,
name: undefined,
};
type MappedType<T> = {
[K in keyof T]?: T[K];
};
const restaurantField: MappedType<Irestaurant> = {
category: undefined,
name: undefined,
};
MappedType을 사용하기 위해선 제네릭 타입 매개변수 T를 명시해주어야한다.
또한 keyof 타입 연산자, 인덱스 접근 타입에 대해서 이해하고 있어야 한다.
keyof T는 T의 모든 키의 유니언 타입을 생성하는 역할을 한다.
만약 위의 예시라면 keyof Irestaurant이 되는것이니 타입은 category | name | distance | description | link 가 된다.
T[K] 같은 경우, T 안에서 K라는 값의 키의 타입을 가져온다.
만약에 위의 예시를 들자면 Irestaurant['name'] 은 string이 된다.
하지만 여기에선 옵셔널 체이닝을 추가해줌으로써 undefined 값들도 허용해주었다.
맵드 타입을 사용한다면 크게 3가지의 장점을 누릴 수 있다.
기존 타입에서 여러 가지 변형된 타입을 만들어야 할 때, 매번 새로운 타입을 직접 정의해야한다.
맵드 타입을 사용하면 기존 타입을 재사용하여 다양한 변형 타입을 쉽게 생성할 수 있다.
타입의 구조가 변경될 때, 모든 관련 타입을 일일이 수정해야 한다.
위로 예로 들면, Irestaurant 타입이 변경되면 저 필드 타입 또한 전부 다 수정해주어야한다.
하지만 맵드 타입을 사용하면, 기본이 되는 타입만 수정하면 되므로 유지 보수가 더 편리하다.
선택적 속성, 읽기 전용 속성 등을 넘어서 타입의 속성을 좀 더 복잡하게 조작해야 하는 경우(예: 특정 속성만 추출하거나 제외, 속성의 타입 변경 등), 맵드 타입을 사용하는 것이 더 적합하다.
Partial<T> 유틸리티 타입을 사용하면 T의 모든 속성을 선택적으로 만들 수 있다.
기본적으로 Partial 타입은 어떠한 T의 모든 속성을 선택적으로 만드는 유틸리티이다.
이는 주어진 타입의 모든 속성에 ?가 추가되어서 해당 속성들이 존재하지 않아도 되거나 undefined의 값을 허용해줄수 있다.
const restaurantField: Partial<IRestaurant> = {
category: undefined,
name: undefined,
distance: undefined,
description: undefined,
};
const restaurantField: Partial<IRestaurant> = {};
Mapped Type과 Partial을 사용하면 아예 빈 객체로 초기화를 해주어도 무방하다.