将受歧视的联盟变成对象

Turning discriminated union into object

有没有办法让受歧视的工会变成这样:

type Something = {
   type: 'mode';
   values: 'first' | 'second';
} | {
   type: 'part';
   values: 'upper' | 'lower';
};

进入

{
    mode: 'first' | 'second';
    part: 'upper' | 'lower';
}

使用一些通用类型?

到目前为止,我尝试过这样的事情:

type MyUnion = {
   type: string;
   values: string;
};

type DiscUnionToObject<U extends MyUnion> = {
   [V in U['type']]: U['values']
}

但是当我 DiscUnionToObject<Something> 它产生

{
    mode: 'first' | 'second' | 'upper' | 'lower';
    part: 'first' | 'second' | 'upper' | 'lower';
}

type 设置为 [=18= 时,'upper' | 'lower' 不是 Something 的一部分,我找不到通用类型 "understand" 的方法].

TypeScript 缺少一些 type operators 你需要做你想做的事。你 喜欢 可以这样说:

type DiscUnionToObject<U extends MyUnion> = {
  [V in U['type']]: (U & { type: V })['values']
}

其中 (U & { type: V }) 交集会从已区分的联合中取出一个元素。例如,如果 USomethingVpart,那么我们谈论的是 道德上的 (Something & { type: 'part' }) 等同于 {type: 'part', values: 'upper'|'lower'},但编译器无法识别这一点:它必须说 'part'&'mode'never,并且任何带有 never- 的对象valued 属性 本身就是 never,但这些减少都没有发生(嗯,不是我们需要的地方)。

所以你不能那样做。您还希望能够迭代联合 and/or 交集并映射每个元素以生成其他联合 and/or 交集,这是映射类型的更通用版本。但你也不能那样做。


TypeScript 在以编程方式 生成 区分联合方面比以编程方式 分析 它们要好得多。因此,根据您的用例,您也许可以执行与您要求的相反的操作。从对象开始并产生并集:

type SomethingObject = {
  mode: 'first' | 'second'
  part: 'upper' | 'lower'
}

type ObjectToDiscUnion<O, V = {
  [K in keyof O]: {type: K, values: O[K]}
}> = V[keyof V]

type Something = ObjectToDiscUnion<SomethingObject>;

您可以验证上面的Something是否与您原来的相同。希望有所帮助;祝你好运!

自 Typescript 2.8 以来,有可能 "pluck out" 可区分联合的单个组件:

Extract<P, { type: 'mode' }>;

多亏了这一点,现在可以完全按照最初的意图去做:

type DiscUnionToObject<U extends MyUnion> = {
    [V in U['type']]: Extract<U, { type: V }>['values'];
}

使用这种类型,当我们执行 DiscUnionToObject<Something> 时,我们得到了我们想要的:

{
    mode: 'first' | 'second';
    part: 'upper' | 'lower';
}