The merge function is a powerful, flexible tool for merging objects, arrays, and their nested properties.
It supports deep merging, merging multiple sources, property descriptor cloning, array merging strategies,
and custom filtering logic.
- Deep Merging: Option to deeply merge objects and arrays.
- Multiple Sources: Merge an array of objects into a target object.
- Merge Arrays: Choose between overwriting, appending, or merging unique array elements.
- Property Descriptors: Clone getter/setter and other property descriptors.
- Custom Filters: Exclude specific properties based on custom logic.
- Security: Built-in protection against prototype pollution (
__proto__,constructor). - TypeScript Ready: Full type safety and intelligent merging of source types.
import { merge } from '@jsopen/objects';
const target = { a: 1, b: 2 };
const source = { b: 3, c: 4 };
merge(target, source);
// target is now: { a: 1, b: 3, c: 4 }const target = { a: 1 };
const sources = [{ b: 2 }, { c: 3 }];
merge(target, sources);
// target is now: { a: 1, b: 2, c: 3 }The merge function accepts an optional Options object as its third argument.
| Option | Type | Default | Description |
|---|---|---|---|
deep |
boolean | 'full' | CallbackFn |
false |
Enables deep merging. true only for plain objects/arrays, 'full' for all objects. |
mergeArrays |
boolean | 'unique' | CallbackFn |
false |
Controls how arrays are merged. |
keepExisting |
boolean | CallbackFn |
false |
If true, does not overwrite existing properties in the target. |
copyDescriptors |
boolean |
false |
If true, copies property descriptors (getters, setters, etc.). |
symbolKeys |
boolean |
true |
Whether to include properties with Symbol keys. |
ignoreUndefined |
boolean |
true |
If true, undefined values in source will be ignored. |
ignoreNulls |
boolean |
false |
If true, null values in source will be ignored. |
ignoreSource |
CallbackFn |
- | Callback to ignore specific source values. |
filter |
CallbackFn |
- | Callback to exclude properties from both target and source. |
true: Merges plain objects and arrays recursively. Non-plain objects (class instances) are assigned by reference.'full': Merges all objects including class instances (excluding built-in types likeDate,RegExp, etc.).CallbackFn: A function(value, context) => booleanto decide whether to go deep for a specific path.
merge(target, source, { deep: true });
// Using a callback to deep merge only specific paths
merge(target, source, {
deep: (val, { path }) => path.startsWith('metadata')
});true: Appends source array elements to the target array.'unique': Appends source array elements and ensures the resulting array has unique values.CallbackFn: A function(value, context) => booleanto decide whether to merge arrays for a specific path.
const target = { tags: ['js'] };
const source = { tags: ['ts', 'js'] };
merge(target, source, { mergeArrays: 'unique' });
// target.tags is now: ['js', 'ts']By default, merge copies values. Enable copyDescriptors to preserve getters, setters, and other attributes.
const source = {
get id() {
return Math.random();
}
};
merge(target, source, { copyDescriptors: true });
// target now has the 'id' getter, not just a static value.merge(target, source, {
// Ignore source values that are booleans
ignoreSource: (val) => typeof val === 'boolean',
// Ignore specific keys
filter: (val, { key }) => key !== 'internalSecret',
// Keep target's value if it already exists
keepExisting: true
});The function automatically skips keys that could lead to prototype pollution:
__proto__constructor
type CallbackFn = (value: any, ctx: CallbackContext) => boolean;
interface CallbackContext {
source: any;
target: any;
key: string | symbol | number;
path: string; // Dot-separated path, e.g., "user.profile.id"
}
interface Options {
deep?: boolean | 'full' | CallbackFn;
symbolKeys?: boolean;
mergeArrays?: boolean | 'unique' | CallbackFn;
keepExisting?: boolean | CallbackFn;
copyDescriptors?: boolean;
ignoreSource?: CallbackFn;
ignoreUndefined?: boolean;
ignoreNulls?: boolean;
filter?: CallbackFn;
}