Skip to content
VersionSize

diff

The diff utility compares two objects and returns an object containing only the properties that were changed or added in the second object. This is ideal for change tracking, auditing, and generating minimal data patches.

Implementation

View Source Code
ts
import { isEqual } from '../typed/isEqual';
import { isObject } from '../typed/isObject';
import type { Obj } from '../types';

/**
 * Computes the difference between two objects.
 *
 * @example
 * ```ts
 * const obj1 = { a: 1, b: 2, c: 3 };
 * const obj2 = { b: 2, c: 3, d: 4 };
 *
 * diff(obj1, obj2); // { d: 4 }
 * ```
 *
 * @param curr - The current object.
 * @param prev - The previous object.
 * @param [compareFn] - A custom function to compare values.
 * @returns An object containing new/modified properties.
 */
export function diff<T extends Obj>(
  curr?: T,
  prev?: T,
  compareFn: (a: unknown, b: unknown) => boolean = isEqual,
): Partial<T> {
  if (!curr && !prev) return {} as Partial<T>;

  const result: Record<string, unknown> = {};

  for (const key of new Set([...Object.keys(curr ?? {}), ...Object.keys(prev ?? {})])) {
    const _curr = curr?.[key];
    const _prev = prev?.[key];

    if (isObject(_curr) && isObject(_prev)) {
      const nestedDiff = diff(_curr, _prev, compareFn) as Partial<T[keyof T]>;
      if (Object.keys(nestedDiff).length > 0) {
        result[key] = nestedDiff;
      }
    } else if (!compareFn(_curr, _prev)) {
      result[key] = _curr;
    }
  }

  return result as Partial<T>;
}

Features

  • Isomorphic: Works in both Browser and Node.js.
  • Minimal Output: Only returns what has actually changed.
  • Deep Comparison: Correctly identifies differences even in nested objects.
  • Type-safe: Preserves typing for partially changed objects.

API

ts
function diff<T extends object, U extends object>(a: T, b: U): Partial<T & U>;

Parameters

  • a: The base (original) object.
  • b: The target (updated) object to compare against the base.

Returns

  • A new object representing the delta between a and b.

Examples

Basic Diff

ts
import { diff } from '@vielzeug/toolkit';

const original = { id: 1, status: 'pending', tags: ['new'] };
const updated = { id: 1, status: 'active', tags: ['new'], priority: 'high' };

const result = diff(original, updated);
// { status: 'active', priority: 'high' }

Nested Object Diff

ts
import { diff } from '@vielzeug/toolkit';

const v1 = {
  user: {
    name: 'Alice',
    settings: { theme: 'dark' },
  },
};

const v2 = {
  user: {
    name: 'Alice',
    settings: { theme: 'light' },
  },
};

diff(v1, v2);
// { user: { settings: { theme: 'light' } } }

Implementation Notes

  • Returns properties from b that are different from a.
  • Uses deep equality (isEqual) for comparisons.
  • If a property exists in a but is missing in b, it is currently not included in the diff (use for "update/patch" logic).
  • Throws TypeError if either argument is not an object.

See Also

  • merge: Combine objects (often used with the result of diff).
  • clone: Create a copy of an object.
  • isEqual: The comparison engine used by diff.