Installation
npx fragmen add object/mergeSource Code
/**
* Checks if a value is a non-array, non-null object.
*
* @param {unknown} item The value to check.
* @returns {boolean} True if the value is a plain object.
*/
function _isObject(item: unknown): item is Record<string, unknown> {
return !!item && typeof item === 'object' && !Array.isArray(item);
}
/**
* Creates a deep copy of an object or array.
*
* @param {T} source The object or array to copy.
* @returns {T} A deep copy of the source.
*/
function _deepCopy<T>(source: T): T {
if (!_isObject(source) && !Array.isArray(source)) {
return source;
}
if (Array.isArray(source)) {
return source.map(item => _deepCopy(item)) as unknown as T;
}
const newObject = {} as Record<keyof T, unknown>;
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
newObject[key as keyof T] = _deepCopy(source[key]);
}
}
return newObject as T;
}
/**
* Deep merges multiple objects into a new object.
*
* Combines objects by merging their properties recursively.
* Later objects in the arguments list override properties of earlier objects.
* Arrays are replaced entirely rather than merged.
*
* @tags pure, object-manipulation
* @template T - An array of object types.
* @param {...T} objects The objects to merge.
* @returns {UnionToIntersection<T[number]>} A new object containing the merged properties, with preserved types.
*/
export function merge<T extends object[]>(
...objects: T
): UnionToIntersection<T[number]> {
const result = {} as Record<string, unknown>;
for (const source of objects) {
if (!_isObject(source)) {
continue;
}
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
const sourceValue = source[key as keyof typeof source];
const targetValue = result[key];
if (_isObject(sourceValue) && _isObject(targetValue)) {
// Recursively merge if both are objects
result[key] = merge(targetValue, sourceValue);
} else {
// Otherwise, overwrite (with a deep copy to prevent mutation)
result[key] = _deepCopy(sourceValue);
}
}
}
}
return result as UnionToIntersection<T[number]>;
}
// Helper type to convert a union of objects into an intersection of objects
// Update unknown to your preference
type UnionToIntersection<U> = (
U extends unknown ? (k: U) => void : never
) extends (k: infer I) => void
? I
: never;
Related Utilities
clone
objectCreates a deep copy of an object. Recursively clones all properties of an object, including nested objects and arrays. Handles circular references by maintaining a reference map. Primitive values, functions, and built-in objects like Date and RegExp are handled appropriately.
deep-equal
objectPerforms deep equality comparison between two values
has-path
objectChecks if a nested property path exists in an object. Safely traverses nested object properties using a dot-notation path string or an array of keys. Returns true if the path exists (even if the final value is undefined), false if any part of the path is missing.
omit
objectCreates a new object by omitting specified keys from the source object. Returns a new object that contains all properties from the source object except for the specified keys. This is the opposite of the pick utility. The operation is shallow - nested objects are not deeply omitted.
pick
objectCreates a new object composed of the picked object properties. Extracts only the specified keys from the source object, creating a new object with just those properties. Non-existent keys are silently ignored.
Quick Actions
Parameters
itemunknownThe value to check.
Returns
booleanTrue if the value is a plain object.