아래와 같은 데이터 타입이 있다.
포괄적인 Schedule
데이터, 그 중 일부만을 사용하는 PremiumSchedule
데이터.
Schedule
에는 있는 타입을 PremiumSchedule
는 모두 사용하지만 그 중 일부는 사용하지 않는다.
타입을 일부만 추출해서 사용하는 5가지 방법을 알아보자.
두가지 문제가 있다
- 전혀 멋지지 않다
- 타입 중복 선언
이런식으로 타입을 늘려가다 타입 수정요청이 들어온다면 감당할 수 없을 것이다.
export interface PremiumSchedule {
startTime: number;
startDate: Date;
endTime: number;
endDate: Date;
}
export interface Schedule extends PremiumSchedule {
season: number;
premiumOn: boolean;
}
가장 간단한 해결책. 하지만 나의 상황에는 의미가 맞지 않았다.
이 데이터 구조상 Schedule
은 더 포괄적인 데이터이고, 그 중 PremiumSchedule
이 특별한 데이터이다.
하지만 이 코드를 처음보는 개발자는 "Premium이 기본값이고 Schedule이 특별한 거구나"라고 생각할 것이다.
의미가 반대가 되는 상황에서는 사용하기 좋을 것 같다.
Schedule 타입 중 일부만 사용하겠다고 명시.
const {
mutateAsync: getPremiumData,
data,
isPending,
} = useGetScheduleData<Partial<Schedule>>();
모든 타입이 옵셔널이 되었다.
하지만 나는 모든 타입이 옵셔널하다는 것을 명시하려는 것이 아니었다.
이 코드를 처음보는 개발자는 "PremiumSchedule 데이터는 다 옵셔널하구나"라고 생각할 것이다.
Premium에는 season
과 premium
만이 없다는 것만을 명확히 해야한다.
여전히 두가지 문제가 있다
- 멋지지 않다
- 명확하지 않다
Schedule이라는 타입을 사용할 건데, 그 중에서 이 타입만 사용할거라고 명시하기.
const {
mutateAsync: getPremiumData,
data,
isPending,
} = useGetScheduleData<
Extract<
Schedule,
{ startTime: number; startDate: Date; endTime: number; endDate: Date }
>
>();
이 코드를 처음 보는 개발자도 해당 타입만 사용한다는 것을 알 수 있다.
하지만 이것도 너무 길고, 타입이 중복된다.
여전히 두가지 문제가 있다
1. 멋지지 않다
2. 타입이 중복된다
Schedule이라는 타입을 사용할 건데, 그 중에서 이 두가지를 제외한 타입을 사용하겠다고 명시한다.
const {
mutateAsync: getPremiumData,
data,
isPending,
} = useLeaderboardTools<Omit<Schedule, "season" | "premium">>();
Typescript가 내 의도대로 타입 추론을 하고 있다.
이 코드를 처음보는 개발자 또한 Schedule이 공통적인 형태이지만, Premium에는 seasion과 premium이 없구나 라고 생각할 수 있을 것이다.
Omit과 비슷하게 동작하지만 쓰이는 경우가 다르다.
Omit은 위와 같이 객체에서 특정 프로퍼티를 제거하는 타입.
하지만 내가 제외하려는 타입이 유니온 타입이라면?
유니온 타입이란?
A || B
type SocialProvider = 'google' | 'discord' | 'apple' | 'facebook'; // union type
type NewRecord = Record<Omit<SocialProvider, 'apple'>, string>;
// Error: Type 'Omit<"google" | "discord" | "apple" | "facebook", "apple">' does not satisfy the constraint 'string | number | symbol'.ts(2344)
따라서 Omit의 key로는 유니온 타입을 넘길 수 없다.
아래처럼 사용해야한다.
type SocialProvider = 'google' | 'discord' | 'apple' | 'facebook';
type NewRecord = Record<Exclude<SocialProvider, 'apple'>, string>;