@tienedev/datype
Version:
Modern TypeScript utility library with pragmatic typing and zero dependencies
90 lines (87 loc) • 2.87 kB
JavaScript
;
/**
* Merges multiple objects into a new object, performing a shallow merge.
* Properties from later objects override properties from earlier objects.
*
* @param objects - The objects to merge
* @returns A new object containing properties from all input objects
*
* @example
* ```typescript
* import { merge } from 'datype';
*
* // Basic merging
* const obj1 = { a: 1, b: 2 };
* const obj2 = { b: 3, c: 4 };
* merge(obj1, obj2); // { a: 1, b: 3, c: 4 }
*
* // Multiple objects
* const obj3 = { d: 5 };
* merge(obj1, obj2, obj3); // { a: 1, b: 3, c: 4, d: 5 }
*
* // Empty objects are handled
* merge({}, { a: 1 }, {}); // { a: 1 }
*
* // With no arguments
* merge(); // {}
*
* // Non-enumerable properties are ignored
* const objWithHidden = {};
* Object.defineProperty(objWithHidden, 'hidden', { value: 'secret', enumerable: false });
* merge({ visible: 1 }, objWithHidden); // { visible: 1 }
*
* // Symbol properties are included
* const sym = Symbol('key');
* const objWithSymbol = { [sym]: 'value', regular: 'prop' };
* merge({}, objWithSymbol); // { [sym]: 'value', regular: 'prop' }
*
* // Shallow merge - nested objects are not deeply merged
* const nested1 = { a: { x: 1, y: 2 } };
* const nested2 = { a: { z: 3 } };
* merge(nested1, nested2); // { a: { z: 3 } } - nested1.a is completely replaced
*
* // Real-world example: configuration merging
* const defaultConfig = {
* timeout: 5000,
* retries: 3,
* headers: { 'Content-Type': 'application/json' }
* };
*
* const userConfig = {
* timeout: 10000,
* headers: { 'Authorization': 'Bearer token' }
* };
*
* const config = merge(defaultConfig, userConfig);
* // {
* // timeout: 10000,
* // retries: 3,
* // headers: { 'Authorization': 'Bearer token' } // Note: completely replaced, not merged
* // }
* ```
*/
function merge(...objects) {
const result = {};
for (const obj of objects) {
if (obj !== null && obj !== undefined && typeof obj === 'object') {
// Copy all enumerable own properties (including Symbol properties)
const propertyDescriptors = Object.getOwnPropertyDescriptors(obj);
for (const key in propertyDescriptors) {
const descriptor = propertyDescriptors[key];
if (descriptor?.enumerable) {
result[key] = obj[key];
}
}
// Copy enumerable Symbol properties
const symbolKeys = Object.getOwnPropertySymbols(obj);
for (const symbolKey of symbolKeys) {
const descriptor = Object.getOwnPropertyDescriptor(obj, symbolKey);
if (descriptor && descriptor.enumerable) {
result[symbolKey] = obj[symbolKey];
}
}
}
}
return result;
}
exports.merge = merge;