如何编写一个泛型类型别名
Combine<A, B>
,它组合了任意两个接口 A 和 B,同时保持两个接口的匹配属性,但使用 union 合并属性类型?
以这两个接口为例:
interface Interface1 {
type: string;
another: bool;
}
interface Interface2 {
type: number;
}
然后
Combine<Interface1, Interface2>
应评估为以下类型:
type Result = {
type: string | number;
another: bool;
}
首先获取 A 中不在 B 中的所有键,以及 B 中不在 A 中的键,并且对于键的交集显式定义并集:
type Combine<A, B> =
Omit<A, keyof B> // items in A that aren't in B
& Omit<B, keyof A> // items in B that aren't in A
& { [K in keyof A & keyof B]: A[K] | B[K] }; // union of both.
请注意,指示一个中存在但另一个中不存在的键可能不存在可能会有所帮助,因此您可以在
Partial
上使用 Omit
:
type Combine<A, B> =
Partial<Omit<A, keyof B>> // possibly items in A that aren't in B
& Partial<Omit<B, keyof A>> // possibly items in B that aren't in A
& { [K in keyof A & keyof B]: A[K] | B[K] }; // union of both.
编辑:Aarium 询问使用联合的问题让我找到了一种更好的方法,因为
T extends any ? X : never
将对联合的每个元素进行操作,您实际上可以扩展联合的任何元素中存在的所有键,然后创建一个与所有接口对应的值的并集,因此归结为:
// gets all viable keys from all interfaces in a union.
type AllKeysOf<T> = T extends any ? keyof T : never
// basically does T[K] but when T is a union it only gives T[K] for the members of the union for which it is a valid key.
type Get<T, K extends keyof any, Fallback=never> = T extends Record<K, any> ? T[K] : Fallback
// takes a union of interfaces and merges them so that any common key is a union of possibilities.
type Combine< T,
setThisToUndefinedToProperlyHandleOptionalFields = never
> = {[K in AllKeysOf<T>]: Get<T,K, setThisToUndefinedToProperlyHandleOptionalFields>}
type TEST = Combine<{a:1} | {a:2, b:10} | {b: 11}>
// TEST = {a: 1|2, b: 10|11}
type TEST2 = Combine<{a:1,b:10} | {a:2},undefined>
// TEST2 = { a: 1|2, b: 10|undefined }
请注意,如果您将
Fallback
中的 Get
generic 设置为 never
,它只会忽略未定义该键的联合元素,但您可以将其设置为 undefined
以指示已定义的键在某些接口中,但并非所有接口最终都可能是未定义的,并不完全说它是可选的,但非常接近。不同处理的确切含义很大程度上取决于您的具体用例,因此我不会在这里详细介绍。 (只需尝试 never
和 undefined
,看看哪一个能提供您想要的行为)