I have a bunch of interfaces with one common field, used as the discriminator for disjoint union. This field is composed of several enums used elsewhere, so I can't make it a single enum in a reasonable way. Something like this (simplified):
const enum Enum1 { key1 = 'key1', key2 = 'key2' }
const enum Enum2 { key1 = 'key1', key3 = 'key3' }
interface Item1 { key: Enum1; value: string; }
interface Item2 { key: Enum2; value: number; }
type Union = Item1 | Item2;
The type is used like this:
let item: Union = getUnionValue();
switch (item.key) {
case Enum1.key1:
// do something knowing the 'item.value' is 'string'
break;
// some other cases
case Enum2.key1:
// do something knowing the 'item.value' is 'number'
break;
// some more cases
}
Of course, when keys in different enums are equivalent, this will lead to breakage at runtime.
Is there any way to check whether the discriminator type Union['key']
is in fact disjoint, i.e. if all the types used are non-intersecting? In other words, I'm looking for the code which would error on the type above, signaling that Enum1.key1
clashes with Enum2.key1
.
I've tried the following:
type Checker<T> = T extends any ?
(Exclude<Union, T>['key'] extends Extract<Union, T>['key'] ? never : any)
: never;
const test: Checker<Union> = null;
hoping to make use of distribution over conditional types, but this doesn't seem to work.