//source: https://github.com/typeorm/typeorm/blob/master/src/util/OrmUtils.ts export class OrmUtils { // Checks if it's an object made by Object.create(null), {} or new Object() private static isPlainObject(item: any) { if (item === null || item === undefined) { return false; } return !item.constructor || item.constructor === Object; } private static mergeArrayKey( target: any, key: number, value: any, memo: Map, ) { // Have we seen this before? Prevent infinite recursion. if (memo.has(value)) { target[key] = memo.get(value); return; } if (value instanceof Promise) { // Skip promises entirely. // This is a hold-over from the old code & is because we don't want to pull in // the lazy fields. Ideally we'd remove these promises via another function first // but for now we have to do it here. return; } if (!this.isPlainObject(value) && !Array.isArray(value)) { target[key] = value; return; } if (!target[key]) { target[key] = Array.isArray(value) ? [] : {}; } memo.set(value, target[key]); this.merge(target[key], value, memo); memo.delete(value); } private static mergeObjectKey( target: any, key: string, value: any, memo: Map, ) { // Have we seen this before? Prevent infinite recursion. if (memo.has(value)) { Object.assign(target, { [key]: memo.get(value) }); return; } if (value instanceof Promise) { // Skip promises entirely. // This is a hold-over from the old code & is because we don't want to pull in // the lazy fields. Ideally we'd remove these promises via another function first // but for now we have to do it here. return; } if (!this.isPlainObject(value) && !Array.isArray(value)) { Object.assign(target, { [key]: value }); return; } if (!target[key]) { Object.assign(target, { [key]: value }); } memo.set(value, target[key]); this.merge(target[key], value, memo); memo.delete(value); } private static merge( target: any, source: any, memo: Map = new Map(), ): any { if (Array.isArray(target) && Array.isArray(source)) { for (let key = 0; key < source.length; key++) { this.mergeArrayKey(target, key, source[key], memo); } } else { for (const key of Object.keys(source)) { this.mergeObjectKey(target, key, source[key], memo); } } } /** * Deep Object.assign. */ static mergeDeep(target: any, ...sources: any[]): any { if (!sources.length) { return target; } for (const source of sources) { OrmUtils.merge(target, source); } return target; } }