我想创建一个具有表示其他接口的属性并集的属性的通用接口。
假设我有两个界面
interface A { something: string; somethingElse: number; } interface B { something: Array<string>; }
我不想将接口写C为
C
interface C { something: string | Array<string>; somethingElse?: number; }
因为这意味着每当我修改接口A或时B,我也需要手动修改接口C。
A
B
根据我在TypeScript文档中看到的内容以及此处关于Stack Overflow的答案,我应该声明一个新类型
type unionOfKeys = keyof A | keyof B;
并实现通用接口形式
interface GenericInterface { <T>(arg: T): T; }
我在朝着
interface C { <T extends unionOfKeys>(arg: T): T extends unionOfKeys ? A[T] | B[T] : any }
但这失败了,因为许多属性与其类型之间不匹配。
我将不胜感激。谢谢。
我认为以下版本的MergeUnion<T>行为可能会符合您的要求:
MergeUnion<T>
type MergeUnion<T> = ( keyof T extends infer K ? [K] extends [keyof T] ? Pick<T, K> & { [P in Exclude<(T extends any ? keyof T : never), K>]?: T extends Partial<Record<P, infer V>> ? V : never } : never : never ) extends infer U ? { [K in keyof U]: U[K] } : never; type C = MergeUnion<A | B>; // type C = { // something: string | string[]; // somethingElse?: number | undefined; } // }
这与其他答案类似,因为它找到T(称为UnionKeys,定义为T extends any ? keyof T : never)的所有组成部分的所有键的并集,并返回其中包含所有键的映射类型。不同之处在于,在这里我们还找到了T(称为IntersectKeys,定义为keyof T)的所有成分的所有键的交集,并将这些键T分为两组键。交集中的一个存在于每个成分中,因此我们可以做Pick<T, IntesectKeys>以获取公共特性。其余的Exclude<UnionKeys, IntersectKeys>在最终类型中是可选的。
T
UnionKeys
T extends any ? keyof T : never
IntersectKeys
keyof T
Pick<T, IntesectKeys>
Exclude<UnionKeys, IntersectKeys
更新2019-08-23:自TS3.5.1起,以下提到的错误似乎已修复
这很丑陋,如果我感觉更好的话,我会清理它。 问题在于,所有成分中出现的任何属性本身都是可选的时,仍然存在问题。TypeScript中存在一个错误(从TS3.5开始),在中{a?: string} | {a?: number},该a属性被视为 必需 属性,如{a: string | number | undefined},而如果任何组成部分将其视为可选,则将其视为可选是更正确的。该错误流到MergeUnion:
{a?: string} | {a?: number}
a
{a: string | number | undefined}
MergeUnion
type Oops = MergeUnion<{a?: string} | {a?: number}> // type Oops = { a: string | number | undefined; }
我在那里没有一个很好的答案,甚至没有更复杂的答案,所以我将在这里停止。
也许这足以满足您的需求。或者,也许@ TitianCernicova-Dragomir的答案足以满足您的需求。希望这些答案对您有所帮助;祝好运!
链接到代码