CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-deepmerge-ts

Deeply merge 2 or more objects respecting type information.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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

Install with Tessl CLI

npx tessl i tessl/npm-deepmerge-ts

docs

customization.md

immutable-merging.md

index.md

mutable-merging.md

utilities.md

tile.json