我有一堆具有一个公共字段的接口,用作不相交联合的区分符。该字段由其他地方使用的几个枚举组成,所以我不能以合理的方式使其成为单个枚举。这样的东西(简化):
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;
该类型的用法如下:
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
}
当然,当不同枚举中的键等效时,将导致运行时损坏。
有没有办法检查鉴别符类型Union['key']
是否实际上是不相交的,即所使用的所有类型是否都是不相交的?换句话说,我正在寻找会在上述类型上出错的代码,表示Enum1.key1
与Enum2.key1
发生冲突。
我尝试了以下操作:
type Checker<T> = T extends any ?
(Exclude<Union, T>['key'] extends Extract<Union, T>['key'] ? never : any)
: never;
const test: Checker<Union> = null;
希望利用条件类型上的分布,但这似乎不起作用。
答案 0 :(得分:1)
这里是一种使用IsUnion
的解决方案,而该解决方案又依赖于UnionToIntersection
:
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type IsUnion<T> =
[T] extends [UnionToIntersection<T>] ? false : true
type DisjointUnion<T extends { key: string }> =
{ [k in T["key"]]: IsUnion<Extract<T, { key: k }>> } extends { [k in T["key"]]: false } ? T : never;
使用具有不同的key
值的并集是可以的:
type Union = DisjointUnion<Item1 | Item2>; // Item1 | Item2
但是添加具有现有键的项,结果类型将为never
:
type Union = DisjointUnion<Item1 | Item2 | Item3>; // never
答案 1 :(得分:0)
一种可能性涉及基于地图/对象类型构建联合,因此不可能添加具有冲突键的东西:
interface Item1 { key: 'key1'; value: string; }
interface Item2 { key: 'key2'; value: number; }
interface Item3 { key: 'key1'; value: Object; }
type UnionAsMap = {
'key1': Item1,
'key2': Item2
};
type Union = UnionAsMap[keyof UnionAsMap]; // Equivalent to Item1 | Item2
这将带来一个问题,您可能会不小心设置了错误的密钥,但是我们可以使用以下代码来强制要求它们的密钥匹配:
type UnionWithMatchingKeys = { [K in keyof UnionAsMap]: UnionAsMap[K] extends { key: K } ? UnionAsMap[K] : never };
type UnionHasMatchingKeys = Union extends UnionWithMatchingKeys[keyof UnionWithMatchingKeys] ? true : false;
let unionCheckSuccess: UnionHasMatchingKeys = true;
因此,如果您不小心做了这样的事情:
interface Item1 { key: 'key3'; value: string; }
UnionHasMatchingKeys
将最终为false,而unionCheckSuccess
行将显示错误。