상호 배타적 타입 설계
Caro-Kann 2.2 버전을 작성하고 있는데 한 가지 문제에 봉착했다. 전역 상태를 스토리지에 저장할 때는 반드시 한 곳을 지정해야 한다. 가령 로컬 스토리지에 저장한다면 세션 스토리지는 지정할 수 없어야 하고, 그 반대도 성립해야 하는 것이다.
type StorageConfig = { local: "???"; session: "???" };
이를 런타임에서 잡아내는 것은 쉽지만, 나는 타입스크립트를 사용해서 미리 타입 에러를 던지고 싶었다. 이 문제를 해결하려면 StorageConfig 타입을 정의할 때 두 필드 중 하나가 정해지면, 다른 하나는 허용하지 않도록 강제해야 한다. 유니온 타입과 never를 사용하면 이 요구사항을 만족할 수 있다.
예를 들어, local이 정의되면 session은 never 타입으로 설정되어 값을 넣을 수 없게 되고, 반대로 session이 정의되면 local은 never 타입이 되어 동일한 제한이 적용된다. 이러한 방식은 컴파일 타임에 명확한 에러를 제공하며, 런타임 비용을 들이지 않고도 타입 안정성을 강화할 수 있다.
type StorageConfig =
| { local: string; session?: never }
| { local?: never; session: string }
스토리지 옵션에 쿠키가 추가되고, 로컬 스토리지와 쿠키를 선택했을 때는 migrate 객체를 포함할 수 있도록 해야한다고 해보자. 이때도 동일한 접근 방법으로 문제를 해결할 수 있다.
type LocalStorageConfig = {
local: string;
session?: never;
cookie?: never;
migrate?: Migrate;
}
type SessionStorageConfig = {
local?: never;
session: string;
cookie?: never;
migrate?: never;
};
type CookieStorageConfig<T> = {
local?: never;
session?: never;
cookie: string;
migrate?: Migrate;
}
type StorageConfig =
| LocalStorageConfig
| SessionStorageConfig
| CookieStorageConfig;
하나를 선택하면 다른 하나는 선택할 수 없다는 점에서 나는 이러한 타입 설계를 상호 배타적 타입 설계(Mutually Exclusive Type Design)라고 부르고 있다.
블로그의 정보
Ayden's journal
Beard Weard Ayden