import { isArray, isObject } from '../../type/type'; import { forOwn } from '../forOwn/forOwn'; /** * Merges U to T. * * @typeParam T - An object to merge to. * @typeParam U - An object to to. * * @return An merged object type. */ export type Merge = Omit & { [ K in ( keyof T & keyof U ) ]: U[ K ] extends object ? U[ K ] extends any[] ? T[ K ] extends any[] ? Array : U[ K ] : T[ K ] extends object ? Merge extends infer A ? Cast : never : U[ K ] : U[ K ]; } & Omit; type Cast = T extends U ? T : U; /** * Recursively merges source properties to the object. * Be aware that this method does not merge arrays. They are just duplicated by `slice()`. * * @param object - An object to merge properties to. * @param source - A source object to merge properties from. * * @return A new object with merged properties. */ export function merge( object: T, source: U ): Merge { forOwn( source, ( value, key ) => { if ( isArray( value ) ) { object[ key ] = value.slice(); } else if ( isObject( value ) ) { object[ key ] = merge( isObject( object[ key ] ) ? object[ key ] : {}, value ); } else { object[ key ] = value; } } ); return object as Merge; }