import { isPlainObject } from 'lodash' ; remarks export function merge < TBase extends Record < string , any > , TSource1 extends Record < string , any > > ( baseObj , source1 ) ; export function merge < TBase extends Record < string , any > , TSource1 extends Record < string , any > , TSource2 extends Record < string , any > > ( baseObj , overrideObj , overrideObj2 ) ; export function merge < TBase extends Record < string , any > , TSource1 extends Record < string , any > , TSource2 extends Record < string , any > , TSource3 extends Record < string , any > > ( baseObj , overrideObj , overrideObj2 ) ; export function merge < TReturn extends Record < string , any > > ( baseObj , ... sources ) { const firstSource = sources [ 0 ] ; if ( firstSource === undefined ) { return baseObj as TReturn ; } return sources . slice ( 1 ) . reduce ( ( merged , nextSource ) => mergeObjects ( merged , nextSource ) , mergeObjects ( baseObj , firstSource ) ) as TReturn ; } const isMergable = ( obj ) => isPlainObject ( obj ) ; const mergeObjects = < T extends Record < string , any > , U extends Record < string , any > > ( baseObj , overrideObj ) : T & U => [ ... new Set ( [ ... Object . keys ( baseObj ) , ... Object . keys ( overrideObj ) ] ) ] . reduce ( ( merged , key ) => { const baseVal = baseObj [ key ] ; const overrideVal = overrideObj [ key ] ; if ( isMergable ( baseVal ) && isMergable ( overrideVal ) ) { merged [ key ] = mergeObjects ( baseVal , overrideVal ) ; } else if ( overrideVal !== undefined ) { merged [ key ] = overrideVal ; } else if ( baseVal !== undefined ) { merged [ key ] = baseVal ; } return merged ; } , { } as any ) ;