0

I am using lodash merge to merge two objects. The types for lodash (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/bab9c9aef2477d222f014a25ce86585af76c3a5c/types/lodash/common/object.d.ts#L1718) simply rely on intersection types which have some shortcomings when it comes to arrays.

Say I want to merge objects with these types

interface One {
  str: string;
  arr: Array<{ foo: string }>;
}

interface Two {
  num: number;
  arr: Array<{ bar: string }>;
}

When these objects are merged with lodash merge the resulting shape is

interface Merged {
  str: string;
  num: number;
  arr: Array<{ foo: string; bar: string }>;
}

But this is not how intersection types work (rationale here). What you end up with for the type of arr is Array<{ foo: string; }> & Array<{ bar: string }> which has this problem:

declare const test: Array<{ foo: string }> & Array<{ bar: string }>;
test.forEach((item) => item.bar); // Property 'bar' does not exist on type '{ foo: string; }'.(2339)

Playground link

Can a better type for merge be defined such that for arrays Array<A> and Array<B> you get Array<A & B> instead of Array<A> & Array<B>? It would have to be some kind of recursive mapped type, but I didn't have any luck writing one.

1 Answer 1

2

I think we can create a type that normalizes arrays recursively.

type NomalizeArrays<T> = {
    [P in keyof T]: T[P] extends any[] ? Array<NomalizeArrays<T[P][number]>> : NomalizeArrays<T[P]>
}

We can pass in any intersection in T. We mostly leave the structure alone except for arrays. For arrays we take T[P][number] which will be an intersection of the object types.

It seems t work as expected, let me know if you find any issues:


let r = _.merge({
    arr: [{ foo : "A"}],
    str: ""
},  {
    arr: [{ bar: "B"}],
    num: 1
})

r.arr.forEach(x => x.bar + x.foo);

let r2 = _.merge({
    arr: [{ outerA: "", nested: [{ foo: "" }]}]
}, {
    arr: [{ outerB: "", nested: [{ bar: "" }]}]
})

r2.arr.forEach(x => {
    x.outerA
    x.outerB
    x.nested.map(xx => xx.bar  +  xx.foo)
});

Playground Link

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.