如果您使用的是 TS3.9+,aggressively reduces intersections of types with conflicting discriminant properties to never,您可以这样写:
// TS 3.9+
type ExtractOverlaps<T, U> = T extends any ? (U & T extends never ? never : T) : never;
type Data = ExtractOverlaps<Test, { selector: "t1" }>;
/* type Data = {
selector: "t1" | "t2" | "t3";
data: any;
} */
在这里,我们将T 拆分为它的联合成员,并且对于每个成员,看看它在与U 相交时是否减少为never。如果是这样,那么我们不想要那个工会成员。如果没有,那么我们确实想要它。这会产生你想要的Data。
如果你还在使用 TS3.8 及以下版本,那么我们可以将减少不兼容对象的交集的操作合成为never,但它很笨拙,我不想讨论所有可能的边缘情况:
// works in TS3.8-
type ReduceNeverPropsToNever<T> =
{ [K in keyof T]-?: [T[K]] extends [never] ? unknown : never }[keyof T] extends
never ? T : never;
type ExtractOverlaps<T, U> = T extends any ? (
ReduceNeverPropsToNever<U & T> extends never ? never : T
) : never;
type Data = ExtractOverlaps<Test, { selector: "t1" }>;
/* type Data = {
selector: "t1" | "t2" | "t3";
data: any;
} */
当然,没有什么说你需要用其他相同形状的东西替换Extract<T, U>。如果你可以分别指定判别属性名称和值,你可以这样做:
type ExtractCompatible<T, K extends keyof T, V extends T[K]> =
T extends any ? [V] extends [T[K]] ? T : never : never;
type Data = ExtractCompatible<Test, "selector", "t1">;
/* type Data = {
selector: "t1" | "t2" | "t3";
data: any;
} */
在这里,我们将T 拆分为其联合成员,并为每个成员检查V 是否可分配给T[K]。我们明确命名属性K 和值V 来寻找,所以我们不必做交叉或其他奇怪的杂耍。这可能是我建议的解决方案,除非您需要直接替换 Extract。
好的,希望对您有所帮助;祝你好运!
Playground link to code