Both Object.assign and Object spread only do a shallow merge.
An example of the problem:
// No object nesting
const x = { a: 1 }
const y = { b: 1 }
const z = { ...x, ...y } // { a: 1, b: 1 }
The output is what you'd expect. However if I try this:
// Object nesting
const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = { ...x, ...y } // { a: { b: 1 } }
Instead of
{ a: { a: 1, b: 1 } }
you get
{ a: { b: 1 } }
x is completely overwritten because the spread syntax only goes one level deep. This is the same with Object.assign()
.
Is there a way to do this?
I would like to present a pretty simple ES5 alternative. The function gets 2 parameters -
target
andsource
that must be of type "object".Target
will be the resulting object.Target
keeps all its original properties but their values may be modified though.cases:
target
doesn't have asource
property,target
gets it;target
does have asource
property andtarget
&source
are not both objects (3 cases out of 4),target
's property gets overriden;target
does have asource
property and both of them are objects/arrays (1 remaining case), then recursion happens merging two objects (or concatenation of two arrays);also consider the following:
It is predictable, supports primitive types as well as arrays and objects. Also as we can merge 2 objects, I think that we can merge more than 2 via reduce function.
take a look at an example (and play around with it if you want):
There is a limitation - browser's call stack length. Modern browsers will throw an error at some really deep level of recursion (think of thousands of nested calls). Also you are free to treat situations like array + object etc. as you wish by adding new conditions and type checks.
I make this method for deep assign using es6.
The deepmerge npm package appears to be the most widely used library for solving this problem: https://www.npmjs.com/package/deepmerge