作为this answer的后续工作,我正在尝试编写一个通用类型,该通用类型将标记映射到已区分联合的一部分。
以上答案中给出的通用版本有效:
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>;
答案 0 :(得分:1)
DiscriminateUnion
中的技术使用distributive conditional type,该功能仅在检查基本的泛型类型参数时才有效。该链接对其进行了很好的解释,或者您可以根据需要阅读longer explanation。获得此行为的唯一方法是对裸通用类型参数 somewhere 进行条件处理。这就是DiscriminatedUnion_1
起作用的原因;您检查了类型参数T
。
幸运的是,您不必使用默认类型参数玩游戏即可获得此效果。泛型类型参数检查必须进行,但不必直接在最终类型别名的 中。
一种实现方法是使用原始的DiscriminateUnion<T, K, V>
定义,并创建一个使用它的别名,例如
type MyDiscriminateUnion<V extends Union["tag"]> = DiscriminateUnion<Union, "tag", V>;
不过,另一种方法是使用名为Extract<T, U>
的预定义类型别名in the standard library,该别名返回与另一个类型{{1 }}。定义如下:
T
这样,您就可以构造U
而不用使用默认类型参数玩游戏了:
type Extract<T, U> = T extends U ? T : never;
好的,希望能有所帮助;祝你好运!