High-performance mutable merging that modifies target objects in place. Ideal for performance-critical applications, large data processing, and scenarios where memory efficiency is important.
Deeply merges objects into a target object, mutating the target with the merge result for optimal performance.
/**
* Deeply merge objects into a target.
* @param target - This object will be mutated with the merge result.
* @param objects - The objects to merge into the target.
*/
function deepmergeInto<T extends object>(
target: T,
...objects: ReadonlyArray<T>
): void;
/**
* Deeply merge objects into a target with full type assertion.
* @param target - This object will be mutated with the merge result.
* @param objects - The objects to merge into the target.
*/
function deepmergeInto<
Target extends object,
Ts extends ReadonlyArray<unknown>,
>(
target: Target,
...objects: Ts
): asserts target is SimplifyObject<
Target &
DeepMergeHKT<
[Target, ...Ts],
DeepMergeMergeFunctionsDefaultURIs,
DeepMergeBuiltInMetaData
>
>;Usage Examples:
import { deepmergeInto } from "deepmerge-ts";
// Basic object merging into target
const config = { api: { timeout: 5000 }, debug: true };
deepmergeInto(config,
{ api: { retries: 3 } },
{ cache: true }
);
// config is now: { api: { timeout: 5000, retries: 3 }, debug: true, cache: true }
// Array merging (concatenation by default)
const targetArray = [1, 2, 3];
deepmergeInto(targetArray, [4, 5], [6, 7]);
// targetArray is now: [1, 2, 3, 4, 5, 6, 7]
// Nested object merging
const state = {
user: { name: "Alice", preferences: { theme: "dark" } },
session: { timeout: 30 }
};
deepmergeInto(state, {
user: { id: 123, preferences: { lang: "en" } },
session: { authenticated: true }
});
// state.user is now: { name: "Alice", id: 123, preferences: { theme: "dark", lang: "en" } }
// state.session is now: { timeout: 30, authenticated: true }
// Performance-critical data processing
const dataProcessor = {
results: new Map<string, number>(),
metadata: { processed: 0, errors: 0 }
};
// Process multiple batches efficiently
const batch1 = {
results: new Map([["item1", 100], ["item2", 200]]),
metadata: { processed: 2, errors: 0 }
};
const batch2 = {
results: new Map([["item3", 300], ["item1", 150]]), // item1 will be overwritten
metadata: { processed: 1, errors: 1 }
};
deepmergeInto(dataProcessor, batch1, batch2);
// dataProcessor.results: Map with [["item1", 150], ["item2", 200], ["item3", 300]]
// dataProcessor.metadata: { processed: 3, errors: 1 } (numbers are overwritten, not summed)Creates a customized mutable merge function with user-defined options for handling different data types and merge strategies.
/**
* Deeply merge two or more objects using the given options.
* @param options - The options on how to customize the merge function.
* @returns A customized merge into function
*/
function deepmergeIntoCustom<BaseTs = unknown>(
options: DeepMergeIntoOptions<
DeepMergeBuiltInMetaData,
DeepMergeBuiltInMetaData
>
): <Target extends object, Ts extends ReadonlyArray<BaseTs>>(
target: Target,
...objects: Ts
) => void;
/**
* Deeply merge two or more objects using the given options and meta data.
* @param options - The options on how to customize the merge function.
* @param rootMetaData - The meta data passed to the root items' being merged.
* @returns A customized merge into function with metadata support
*/
function deepmergeIntoCustom<
BaseTs = unknown,
MetaData = DeepMergeBuiltInMetaData,
MetaMetaData extends DeepMergeBuiltInMetaData = DeepMergeBuiltInMetaData,
>(
options: DeepMergeIntoOptions<MetaData, MetaMetaData>,
rootMetaData?: MetaData
): <Target extends object, Ts extends ReadonlyArray<BaseTs>>(
target: Target,
...objects: Ts
) => void;Usage Examples:
import { deepmergeIntoCustom } from "deepmerge-ts";
// Custom array merging - replace instead of concatenate
const mergeIntoReplace = deepmergeIntoCustom({
mergeArrays: (target, arrays) => {
// Clear target and set to last array
target.value.length = 0;
if (arrays.length > 0) {
target.value.push(...arrays[arrays.length - 1]);
}
}
});
const result = { items: [1, 2, 3] };
mergeIntoReplace(result, { items: [4, 5] });
// result.items is now: [4, 5]
// Custom object merging with property filtering
const mergeIntoFiltered = deepmergeIntoCustom({
mergeRecords: (target, records, utils, meta) => {
for (const key of utils.getKeys(records)) {
if (key.startsWith('_')) {
continue; // Skip private properties
}
const values = records
.map(r => r[key])
.filter(v => v !== undefined);
if (values.length > 0) {
if (target.value[key] !== undefined) {
values.unshift(target.value[key]);
}
utils.mergeUnknownsInto(
{ value: target.value, key },
values,
utils,
utils.metaDataUpdater(meta, { key, values })
);
}
}
}
});
const target = { name: "Alice", _secret: "hidden", data: { count: 1 } };
mergeIntoFiltered(target,
{ name: "Bob", _secret: "ignored", data: { count: 2, flag: true } }
);
// target: { name: "Bob", _secret: "hidden", data: { count: 2, flag: true } }
// Performance optimization for large datasets
const mergeIntoBulk = deepmergeIntoCustom({
enableImplicitDefaultMerging: true,
mergeRecords: (target, records, utils, meta) => {
// Batch property updates for better performance
const updates = new Map();
for (const record of records) {
for (const [key, value] of Object.entries(record)) {
updates.set(key, value);
}
}
for (const [key, value] of updates) {
if (typeof value === 'object' && value !== null &&
typeof target.value[key] === 'object' && target.value[key] !== null) {
// Recursive merge for nested objects
utils.deepmergeInto(target.value[key], value);
} else {
target.value[key] = value;
}
}
}
});Configuration options specifically designed for mutable merge operations.
/**
* The options the user can pass to customize deepmergeInto.
*/
type DeepMergeIntoOptions<
in out M,
MM extends Readonly<Record<PropertyKey, unknown>> = {}
> = Partial<{
/** Custom function for merging objects/records into target */
mergeRecords: DeepMergeMergeIntoFunctions<M, MM>["mergeRecords"] | false;
/** Custom function for merging arrays into target */
mergeArrays: DeepMergeMergeIntoFunctions<M, MM>["mergeArrays"] | false;
/** Custom function for merging Maps into target */
mergeMaps: DeepMergeMergeIntoFunctions<M, MM>["mergeMaps"] | false;
/** Custom function for merging Sets into target */
mergeSets: DeepMergeMergeIntoFunctions<M, MM>["mergeSets"] | false;
/** Custom function for merging other types into target */
mergeOthers: DeepMergeMergeIntoFunctions<M, MM>["mergeOthers"];
/** Function to update metadata during merging */
metaDataUpdater: (previousMeta: M | undefined, metaMeta: Readonly<Partial<MM>>) => M;
/** Enable falling back to default merge when custom functions return undefined */
enableImplicitDefaultMerging: boolean;
}>;
/**
* Utility interface provided to mutable merge functions
*/
interface DeepMergeMergeIntoFunctionUtils<M, MM> {
/** Default merge into function implementations */
defaultMergeFunctions: DeepMergeMergeIntoFunctionsDefaults;
/** Current merge into function configuration */
mergeFunctions: DeepMergeMergeIntoFunctions<M, MM>;
/** Metadata updater function */
metaDataUpdater: (previousMeta: M | undefined, metaMeta: Readonly<Partial<MM>>) => M;
/** Recursive deepmergeInto function for nested merging */
deepmergeInto: (target: object, ...objects: readonly unknown[]) => void;
/** Utility for merging unknown values into target */
mergeUnknownsInto: (
target: Reference<unknown>,
values: ReadonlyArray<unknown>,
utils: DeepMergeMergeIntoFunctionUtils<M, MM>,
meta: M | undefined
) => void;
/** Whether implicit default merging is enabled */
useImplicitDefaultMerging: boolean;
/** Special action symbols */
actions: typeof actionsInto;
}The default mutable merge strategies that modify targets in place.
/**
* Default merge into functions interface
*/
type DeepMergeMergeIntoFunctionsDefaults = {
/** Merge plain objects into target by recursively merging properties */
mergeRecords: <
Ts extends ReadonlyArray<Record<PropertyKey, unknown>>,
U extends DeepMergeMergeIntoFunctionUtils<M, MM>,
M,
MM extends DeepMergeBuiltInMetaData
>(
target: Reference<Record<PropertyKey, unknown>>,
values: Ts,
utils: U,
meta: M | undefined
) => void;
/** Merge arrays into target by appending all elements */
mergeArrays: <
Ts extends ReadonlyArray<ReadonlyArray<unknown>>,
U extends DeepMergeMergeIntoFunctionUtils<M, MM>,
M,
MM extends DeepMergeBuiltInMetaData
>(
target: Reference<unknown[]>,
values: Ts,
utils: U,
meta: M | undefined
) => void;
/** Merge Sets into target by adding all elements */
mergeSets: <
Ts extends ReadonlyArray<ReadonlySet<unknown>>,
U extends DeepMergeMergeIntoFunctionUtils<M, MM>,
M,
MM extends DeepMergeBuiltInMetaData
>(
target: Reference<Set<unknown>>,
values: Ts,
utils: U,
meta: M | undefined
) => void;
/** Merge Maps into target by setting all key-value pairs */
mergeMaps: <
Ts extends ReadonlyArray<ReadonlyMap<unknown, unknown>>,
U extends DeepMergeMergeIntoFunctionUtils<M, MM>,
M,
MM extends DeepMergeBuiltInMetaData
>(
target: Reference<Map<unknown, unknown>>,
values: Ts,
utils: U,
meta: M | undefined
) => void;
/** Merge other types into target by setting to the last non-undefined value */
mergeOthers: <
Ts extends ReadonlyArray<unknown>,
U extends DeepMergeMergeIntoFunctionUtils<M, MM>,
M,
MM extends DeepMergeBuiltInMetaData
>(
target: Reference<unknown>,
values: Ts,
utils: U,
meta: M | undefined
) => void;
};Supporting types and constants for mutable operations.
/**
* Reference wrapper for mutable operations
*/
type Reference<T> = { value: T };
/**
* Special values that tell deepmergeInto to perform certain actions
*/
const actionsInto: {
/** Symbol to indicate default merge behavior should be used */
readonly defaultMerge: symbol;
};
/**
* Simplify complex intersection types into single objects
*/
type SimplifyObject<T extends {}> = {
[K in keyof T]: T[K];
} & {};Mutable merging offers several performance advantages:
Best Practices:
// Good: Reuse target objects for multiple merges
const processor = { data: new Map(), stats: { count: 0 } };
for (const batch of batches) {
deepmergeInto(processor, batch);
}
// Good: Use mutable merging for performance-critical paths
function processLargeDataset(target: DataStructure, chunks: DataChunk[]) {
const mergeChunk = deepmergeIntoCustom({
mergeArrays: (target, arrays) => {
// Optimize for array concatenation
for (const arr of arrays) {
target.value.push(...arr);
}
}
});
for (const chunk of chunks) {
mergeChunk(target, chunk);
}
}
// Avoid: Creating new merge functions repeatedly
// Instead, create once and reuse
const customMerge = deepmergeIntoCustom({ /* options */ });