简体   繁体   English

TypeScript 定义合并对象,但在 object 属性中防止合并不同类型

[英]TypeScript definition to merge objects, but prevent the merging of different types in the object properties

I want to write a function that extends a object with another object.我想写一个 function 扩展一个 object 和另一个 object。 For example: We will call one object src and the other ext .例如:我们将调用一个 object src和另一个ext The function has to return (a copy) of the src object but deeply (recursively) extends the object with the ext object. function 必须返回(副本) src object 但深度(递归)扩展 object 与ext objectAC If the data type of one (sub-) property of the ext doesn't matches the type of the src (sub-) property the function has to ignore the ext value.如果ext的一个(子)属性的数据类型与src (子)属性的类型不匹配,则 function 必须忽略ext值。 New properties from ext (not existing in src ) will be added to the result object.来自ext的新属性(不存在于src中)将被添加到结果 object 中。

For better understanding here an complete example:为了更好地理解这里一个完整的例子:

/** 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` */
}

TypeScript Playground Link TypeScript 游乐场链接

I know how to write the function.我知道如何编写 function。 That is easy but I don't know how to write this type definition.这很容易,但我不知道如何编写这种类型定义。 Is that even possible?这甚至可能吗?

The default TypeScript type inference behavior doesn't fit my problem because the Types are recursive and more complex than the simple object type.默认的 TypeScript 类型推断行为不适合我的问题,因为类型是递归的,并且比简单的object类型更复杂。 I will use the function for example to load user specific configurations into my app.例如,我将使用 function 将用户特定的配置加载到我的应用程序中。 The user configuration could be corrupted.用户配置可能已损坏。 So I have to merge the default configuration with the user specific configuration.所以我必须将默认配置与用户特定配置合并。

I will interpret what you're asking for as the following: for object types T and U , the object type SafelyMergedObject<T, U> should have the same keys as T & U , but with some differences to the property types.我将您的要求解释如下:对于 object 类型TU , object 类型SafelyMergedObject<T, U>应该具有与T & U相同的键,但与属性类型有一些差异。 If a key K exists in just T or U but not both, then use the property as-is (so this is the same as T & U ).如果键K仅存在于TU中,但不存在于两者中,则按原样使用该属性(因此这与T & U相同)。 If the key K exists in both T and U and at least one of the two property types is not an object, then use the property type from T and ignore the property from U .如果键KTU中都存在,并且这两种属性类型中的至少一种不是 object,则使用T中的属性类型并忽略U中的属性。 If the key K exists in both T and U and is an object type in both T and U , then recurse down into that property via SafelyMergedObject<T[K], U[K]> .如果键KTU中都存在,并且在TU都是object 类型,则通过SafelyMergedObject<T[K], U[K]>向下递归到该属性。

This gets translated into something like:这被翻译成如下内容:

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;

Here we are first outputting Omit<U, keyof T> , which is the properties from U that do not exist in T .这里我们首先输出Omit<U, keyof T> ,这是 U 中不存在于T中的属性。 Then we are going through the keys of T , and outputting T[K] if the property is not in U , or if it is in U but at least one of T[K] or U[K] is not an object.然后我们遍历T的键,如果属性不在U中,或者如果它在U中但T[K]U[K]中的至少一个不是 object,则输出T[K]

The only "trick" here is extends infer O? {[K in keyof O]: O[K]}: never这里唯一的“技巧”是extends infer O? {[K in keyof O]: O[K]}: never extends infer O? {[K in keyof O]: O[K]}: never . extends infer O? {[K in keyof O]: O[K]}: never All this does is "prettify" or "expand" the object type by iterating over all the keys and merging the result into a single object type.所有这一切都是通过迭代所有键并将结果合并到单个 object 类型来“美化”或“扩展” object 类型。

Let's see it in action with your src and ext values:让我们看看它与您的srcext值的作用:

type Result = SafelyMergedObject<typeof src, typeof ext>;

If you hover over that with IntelliSense you'll see:如果您使用 IntelliSense 超过 hover,您将看到:

type Result = {
    e: string;
    a: string;
    b: string;
    c: boolean;
    d: {
        c: string;
        a: string;
        b: string;
    };
}

which is, I think, what you wanted.我想,这就是你想要的。 Note that if I didn't include the extends infer O... line, the Result type would be evaluated as:请注意,如果我没有包含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;
    }>;
}

which is significantly harder to understand, although it amounts to the same type.这明显更难理解,尽管它属于同一类型。


Note that there are probably all sorts of edge cases that will crop up if you use the above SafelyMergedObject<T, U> in different situations.请注意,如果您在不同情况下使用上述SafelyMergedObject<T, U> ,可能会出现各种边缘情况。 You'll need to decide what you want the output to look like in those situations and possibly tweak the definition to get those to happen.您需要确定您希望 output 在这些情况下的样子,并可能调整定义以实现这些目标。 So be careful.所以要小心。

Okay, hope that helps;好的,希望有帮助; good luck!祝你好运!

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM