or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

customization.mdimmutable-merging.mdindex.mdmutable-merging.mdutilities.md
tile.json

mutable-merging.mddocs/

Mutable Deep Merging

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.

Capabilities

Basic Mutable Merge

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)

Custom Mutable Merge

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;
      }
    }
  }
});

Mutable Merge Options

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;
}

Default Mutable Merge Functions

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;
};

Reference Type and Actions

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];
} & {};

Performance Considerations

Mutable merging offers several performance advantages:

  • Memory Efficiency: No new objects created for the target
  • Reduced Garbage Collection: Less memory allocation and deallocation
  • In-Place Updates: Optimal for large data structures
  • Bulk Operations: Efficient for processing multiple merge operations

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 */ });