将标记映射到标记的联合成员不适用于特定联合,仅适用于通用联合
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!
好的,希望对您有所帮助;祝你好运!
作为
以上答案给出的通用版本有效:
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>;
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!
好的,希望对您有所帮助;祝你好运!