![](/img/trans.png)
[英]Typescript - merge overlapping properties of objects with different interface
[英]TypeScript definition to merge objects, but prevent the merging of different types in the object properties
我想写一个 function 扩展一个 object 和另一个 object。 例如:我们将调用一个 object src
和另一个ext
。 function 必须返回(副本) src
object 但深度(递归)扩展 object 与ext
objectAC 如果ext
的一个(子)属性的数据类型与src
(子)属性的类型不匹配,则 function 必须忽略ext
值。 来自ext
的新属性(不存在于src
中)将被添加到结果 object 中。
为了更好地理解这里一个完整的例子:
/** A helper definition to build indexable objects */
interface indexObject<T> {
[key: string]: T
}
type SafelyMergedObject<Src extends indexObject<any>, Ext extends indexObject<any>> = {
// The searched type definition
}
const src= {
a: "a",
b: "b",
c: false,
d: {
a: "Alice",
b: "Bob",
}
}
const ext = {
a: ["a"],
b: 1,
c: true,
d: {
a: "Ann",
c: "Clair",
},
e: "New value",
}
const result: SafelyMergedObject<typeof src, typeof ext> = {
a: "a", /** Only string should be allowed here because
it's the property type of the source (Src) type */
b: "b", /** Same as above (`a` property) */
c: true,/** Since `c` has the same data type the function
should return the property value of the `ext`
object */
d: { /** Same data type in both objects */
a: "Ann", /** new value from `ext` */
b: "Bob", /** copied value from `src` */
c: "Clair", /** new property from `ext` */
},
e: "New Value", /** new property from `ext` */
}
我知道如何编写 function。 这很容易,但我不知道如何编写这种类型定义。 这甚至可能吗?
默认的 TypeScript 类型推断行为不适合我的问题,因为类型是递归的,并且比简单的object
类型更复杂。 例如,我将使用 function 将用户特定的配置加载到我的应用程序中。 用户配置可能已损坏。 所以我必须将默认配置与用户特定配置合并。
我将您的要求解释如下:对于 object 类型T
和U
, object 类型SafelyMergedObject<T, U>
应该具有与T & U
相同的键,但与属性类型有一些差异。 如果键K
仅存在于T
或U
中,但不存在于两者中,则按原样使用该属性(因此这与T & U
相同)。 如果键K
在T
和U
中都存在,并且这两种属性类型中的至少一种不是 object,则使用T
中的属性类型并忽略U
中的属性。 如果键K
在T
和U
中都存在,并且在T
和U
中都是object 类型,则通过SafelyMergedObject<T[K], U[K]>
向下递归到该属性。
这被翻译成如下内容:
type SafelyMergedObject<T, U> = (
Omit<U, keyof T> & { [K in keyof T]:
K extends keyof U ? (
[U[K], T[K]] extends [object, object] ?
SafelyMergedObject<T[K], U[K]>
: T[K]
) : T[K] }
) extends infer O ? { [K in keyof O]: O[K] } : never;
这里我们首先输出Omit<U, keyof T>
,这是 U 中不存在于T
中的属性。 然后我们遍历T
的键,如果属性不在U
中,或者如果它在U
中但T[K]
或U[K]
中的至少一个不是 object,则输出T[K]
。
这里唯一的“技巧”是extends infer O? {[K in keyof O]: O[K]}: never
extends infer O? {[K in keyof O]: O[K]}: never
。 所有这一切都是通过迭代所有键并将结果合并到单个 object 类型来“美化”或“扩展” object 类型。
让我们看看它与您的src
和ext
值的作用:
type Result = SafelyMergedObject<typeof src, typeof ext>;
如果您使用 IntelliSense 超过 hover,您将看到:
type Result = {
e: string;
a: string;
b: string;
c: boolean;
d: {
c: string;
a: string;
b: string;
};
}
我想,这就是你想要的。 请注意,如果我没有包含extends infer O...
行,则Result
类型将被评估为:
type Result = Pick<{
a: string[];
b: number;
c: boolean;
d: {
a: string;
c: string;
};
e: string;
}, "e"> & {
a: string;
b: string;
c: boolean;
d: SafelyMergedObject<{
a: string;
b: string;
}, {
a: string;
c: string;
}>;
}
这明显更难理解,尽管它属于同一类型。
请注意,如果您在不同情况下使用上述SafelyMergedObject<T, U>
,可能会出现各种边缘情况。 您需要确定您希望 output 在这些情况下的样子,并可能调整定义以实现这些目标。 所以要小心。
好的,希望有帮助; 祝你好运!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.