我的代码中有一个类型
type Test = {
selector:
| "t1"
| "t2"
| "t3"; // and t4 t5 ...
data: any;
}
| {
selector: "temp";
data: any;
prev: any;
};
所以我想知道如何从这种类型中提取t1
?
我已经看到以下答案:https://stackoverflow.com/a/52943170/1827594
这很好
type Data = Extract<Test , { selector: "t1" | "t2" | "t3" /* and t4 t5 */ }>
但这不起作用
type Data = Extract<Test , { selector: "t1" }> // the type of Data is never
因此,如果它是两个以上的并集类型(t3,t4和...),则必须重复所有这些键。
有什么方法可以在不提供t1
的情况下从Test
提取t2,t3,...
吗?
更新
Test
类型简化了问的过程,我正在寻找实现这一目标的 dynamic 方法,并且与Test
中的其他字段无关(因为类型更改频繁)
答案 0 :(得分:2)
您可以编写自定义提取函数并使用the infer keyword:
type Test = {
selector:
| "t1"
| "t2"
| "t3"; // and t4 t5 ...
data: number;
}
| {
selector: "temp";
data: any;
prev: any;
};
type TestExtract<T> = T extends { selector: "t1" | "t2" | "t3", data: infer U }
? { selector: "t1", data: U }
: never;
type Data = TestExtract<Test> // {selector: "t1" , data: number };
答案 1 :(得分:1)
如果Test
联合只包含这两个定义明确的成员
如果Test
联合仅包含原始帖子中描述的成员,则最简单的解决方案是解决相反的问题。 提取工会的第一位成员,而不是排除工会的第二位成员。
type Data = Exclude<Test, { selector: "temp" }>
如果Test
联合包含任意数量的成员
此解决方案较为复杂,但也适用于较大的工会。
interface Selectable {
selector: string;
}
type Extractor<T extends Selectable, U extends Selectable> =
// For each member of the union T...
T extends any
// See if the member overlaps with U.
? U extends Pick<T, 'selector'>
// If it does, return that member of the union.
? T
// If not, return never.
: never
: never;
type Data = Extractor<Test, { selector: 't1' }>;
请注意T extends any
的使用。看起来很尴尬-毕竟,每种类型都扩展any
,因此始终满足此条件。但是,使用条件类型使您可以迭代我们想要的联合的每个成员。进一步了解distributive conditional types。
答案 2 :(得分:1)
如果您使用的是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
分成其联合成员,并针对每个成员,查看与never
相交时它是否减少为U
。如果是这样,那么我们就不需要那个工会成员。如果没有,那么我们确实想要它。这将产生您想要的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
,否则这可能是我建议的解决方案。
好的,希望能有所帮助;祝你好运!