JeB
JeB

Reputation: 12133

Deep merge two class instances

How do I properly deep merge (lodash like) two ES6 class instances?
The resulting object has to be an actual instance of the same class and its properties should be a deep merge of the two instances' properties.

Upvotes: 2

Views: 1443

Answers (2)

Adrien Mangeot
Adrien Mangeot

Reputation: 21

Lodash merge did not work for me as is for nested classes. See this test:

class DeepEntity {
  @Exclude({ toPlainOnly: true })
  public id: string;
  public test: string;
}

class Entity {
  public id: string;
  public test: string;
  public deep: DeepEntity;
}

class DeepDto {
  public test: string;
}

class Dto {
  public test: string;
  public deep: DeepDto;
}

const entity = plainToInstance(Entity, {
  id: 'uuid',
  test: 'entity',
  deep: plainToInstance(DeepEntity, {
    id: 'deep-uuid',
    test: 'deep-entity',
  }),
});

const dto = plainToInstance(Dto, {
  id: 'uuid',
  test: 'dto',
  deep: plainToInstance(DeepDto, {
    id: 'deep-uuid',
    test: 'deep-dto',
  }),
});

const merged = merge(entity, dto);

// WNG: The following fails.
expect(merged.deep instanceof DeepEntity).toBe(true);

What worked for me was to use mergeWith with a customizer like this one:

import { instanceToPlain } from 'class-transformer';
import { isArray, isObjectLike, mergeWith } from 'lodash';

function customizer(objArray, srcArray, key) {
  // NOTE: Cast src to a plain object to prevent lodash from overriding prototypes.
  if (isObjectLike(srcArray) && !isArray(srcArray))
    return mergeWith(
      objArray,
      instanceToPlain(srcArray),
      customizer({ idCustomizers, defaultIdCustomizer }),
    );
}

Upvotes: 0

JeB
JeB

Reputation: 12133


If there is no need to create a new instance, the following will do

_.merge(instance1, instance2)

This will deep merge instance2 properties into instance1 while preserving the prototype.


If the merged instance should be a brand new object it is still achievable:

let newInstance = Object.assign(Object.create(Object.getPrototypeOf(o1)), _.merge(o1, o2));

This will create a new object which is an instance of the same class o1 is and will deep merge into it the properties of o1 and o2.
This approach, however, has some caveats, for example (from here):

It's hardly possible if the instance was created with heavy use of closures in the constructor function. We may never now which internal values were set, and how to reproduce such a setup.

Upvotes: 1

Related Questions