将标记映射到标记的联合成员不适用于特定联合,仅适用于通用联合

mapping tag to tagged union member doesn't work for specific union, only generic one

作为 的后续行动,我正在尝试编写一个通用类型,将标记映射到属于可区分联合的类型。

以上答案给出的通用版本有效:

type DiscriminateUnion<T, K extends keyof T, V extends T[K]> = T extends Record<K, V> ? T : never

但我无法制作自己的版本,该版本不通用 T(工会本身)。如果我使联合类型具有默认值,它 确实 工作,我觉得这很奇怪。

这是我的代码:

interface TypeA {
  tag: "a";
  data: string;
}

interface TypeB {
  tag: "b";
  data: [number];
}

interface TypeCD {
  tag: "c" | "d";
  data: number;
}

type Union = TypeA | TypeB | TypeCD;

type DiscriminatedUnion_0<V extends Union["tag"]> = Union extends Record<"tag", V> ? Union : never;
let shouldBeTypeA_0: DiscriminatedUnion_0<"a">; // doesn't work, type 'never'

// this works
type DiscriminatedUnion_1<V extends Union["tag"], T extends Union = Union> = T extends Record<"tag", V> ? T : never;
let shouldBeTypeA_1: DiscriminatedUnion_1<"a">;

type DiscriminatedUnion_2<V extends Union["tag"], T extends Union> = T extends Record<"tag", V> ? T : never;
let shouldBeTypeA_2: DiscriminatedUnion_2<"a", Union>;

uses a distributive conditional type which only works if you are checking a bare generic type parameter. That link explains it pretty well, or you can read a 中的技巧,如果你愿意的话。获得此行为的唯一方法是在裸泛型类型参数 某处 上执行条件。这就是 DiscriminatedUnion_1 起作用的原因;你检查了类型参数 T.

幸运的是,您不必玩具有默认类型参数的游戏即可获得此效果。通用类型参数检查必须发生,但它不必在最终类型别名中 直接 中。

一种方法是使用原始的 DiscriminateUnion<T, K, V> 定义并制作一个使用它的别名,例如

type MyDiscriminateUnion<V extends Union["tag"]> = DiscriminateUnion<Union, "tag", V>;

不过,另一种方法是使用预定义的类型别名 in the standard library,称为 Extract<T, U>,returns 联合类型的所有成分 T匹配另一种类型 U。定义如下:

type Extract<T, U> = T extends U ? T : never;

有了它,您可以构建您的 DiscriminatedUnion 而无需使用默认类型参数玩游戏:

type DiscriminatedUnion<V extends Union["tag"]> = Extract<Union, Record<"tag", V>>;
let shouldBeTypeA: DiscriminatedUnion<"a">; // TypeA, hooray!

好的,希望对您有所帮助;祝你好运!