A blazing fast equality comparison library for JavaScript and TypeScript
npx @tessl/cli install tessl/npm-fast-equals@4.0.0Fast Equals is a blazing fast equality comparison library for JavaScript and TypeScript that provides both deep and shallow equality comparisons. It handles complex data types including NaN, Date objects, RegExp objects, Map/Set collections, Promise objects, and React elements with specialized high-performance algorithms and optional circular reference support.
npm install fast-equalsimport { deepEqual, shallowEqual, circularDeepEqual, circularShallowEqual, sameValueZeroEqual, createCustomEqual, createCustomCircularEqual } from "fast-equals";For CommonJS:
const { deepEqual, shallowEqual, circularDeepEqual, circularShallowEqual, sameValueZeroEqual, createCustomEqual, createCustomCircularEqual } = require("fast-equals");import { deepEqual, shallowEqual } from "fast-equals";
// Deep equality comparison
const objA = { foo: { bar: "baz" } };
const objB = { foo: { bar: "baz" } };
console.log(objA === objB); // false
console.log(deepEqual(objA, objB)); // true
// Shallow equality comparison
const nestedObject = { bar: "baz" };
const shallowA = { foo: nestedObject };
const shallowB = { foo: nestedObject };
const shallowC = { foo: { bar: "baz" } };
console.log(shallowEqual(shallowA, shallowB)); // true (same reference)
console.log(shallowEqual(shallowA, shallowC)); // false (different references)Fast Equals is built around several key components:
Performs comprehensive deep equality comparison on objects with full type support and optimized performance.
/**
* Whether the items passed are deeply-equal in value.
* @param a - First value to compare
* @param b - Second value to compare
* @returns Boolean indicating deep equality
*/
function deepEqual<A, B>(a: A, b: B): boolean;Usage Examples:
import { deepEqual } from "fast-equals";
// Primitive comparison
deepEqual(42, 42); // true
deepEqual("hello", "hello"); // true
deepEqual(NaN, NaN); // true
// Object comparison
deepEqual({ a: 1, b: { c: 3 } }, { a: 1, b: { c: 3 } }); // true
deepEqual([1, [2, 3]], [1, [2, 3]]); // true
// Date comparison
deepEqual(new Date("2023-01-01"), new Date("2023-01-01")); // true
// Map comparison (keys and values are compared deeply)
const map1 = new Map([[{ key: "value" }, "data"]]);
const map2 = new Map([[{ key: "value" }, "data"]]);
deepEqual(map1, map2); // true
// Set comparison
deepEqual(new Set([1, 2, 3]), new Set([3, 2, 1])); // truePerforms shallow equality comparison where nested objects are compared by reference only.
/**
* Whether the items passed are shallowly-equal in value.
* @param a - First value to compare
* @param b - Second value to compare
* @returns Boolean indicating shallow equality
*/
function shallowEqual<A, B>(a: A, b: B): boolean;Usage Examples:
import { shallowEqual } from "fast-equals";
const shared = { nested: "value" };
const obj1 = { a: 1, b: shared };
const obj2 = { a: 1, b: shared };
const obj3 = { a: 1, b: { nested: "value" } };
shallowEqual(obj1, obj2); // true (same references)
shallowEqual(obj1, obj3); // false (different references for b)
// Array shallow comparison
shallowEqual([1, 2, shared], [1, 2, shared]); // true
shallowEqual([1, 2, { x: 1 }], [1, 2, { x: 1 }]); // falseSpecialized equality comparisons that handle circular references safely using WeakMap-based caching.
/**
* Whether the items passed are deeply-equal in value, including circular references.
* @param a - First value to compare
* @param b - Second value to compare
* @returns Boolean indicating deep equality with circular support
*/
function circularDeepEqual<A, B>(a: A, b: B): boolean;
/**
* Whether the items passed are shallowly-equal in value, including circular references.
* @param a - First value to compare
* @param b - Second value to compare
* @returns Boolean indicating shallow equality with circular support
*/
function circularShallowEqual<A, B>(a: A, b: B): boolean;Usage Examples:
import { circularDeepEqual, circularShallowEqual } from "fast-equals";
// Circular object example
function createCircular(value: string) {
const obj: any = {
me: {
deeply: {
nested: {
reference: null, // Will be set to obj
},
},
},
value,
};
obj.me.deeply.nested.reference = obj;
return obj;
}
const circular1 = createCircular("foo");
const circular2 = createCircular("foo");
const circular3 = createCircular("bar");
circularDeepEqual(circular1, circular2); // true
circularDeepEqual(circular1, circular3); // false
// Circular array example
const array: any[] = ["foo"];
array.push(array);
circularShallowEqual(array, ["foo", array]); // truePerforms SameValueZero comparison according to ECMAScript specification.
/**
* Whether the values passed are strictly equal or both NaN.
* @param a - First value to compare
* @param b - Second value to compare
* @returns Boolean indicating SameValueZero equality
*/
function sameValueZeroEqual<A, B>(a: A, b: B): boolean;Usage Examples:
import { sameValueZeroEqual } from "fast-equals";
sameValueZeroEqual(1, 1); // true
sameValueZeroEqual(NaN, NaN); // true
sameValueZeroEqual(0, -0); // true
sameValueZeroEqual(1, "1"); // false
sameValueZeroEqual({}, {}); // false (different object references)Factory functions for creating custom equality comparators with user-defined behavior for specific use cases.
/**
* Create a custom equality comparison method.
* @param getComparatorOptions - Function that receives default options and returns custom overrides
* @returns Custom equality comparator function
*/
function createCustomEqual<Meta = undefined>(
getComparatorOptions: GetComparatorOptions<Meta>
): EqualityComparator<Meta>;
/**
* Create a custom equality comparison method that handles circular references.
* @param getComparatorOptions - Function that receives default options and returns custom overrides
* @returns Custom circular equality comparator function
*/
function createCustomCircularEqual<Meta extends BaseCircularMeta = WeakMap<any, any>>(
getComparatorOptions: GetComparatorOptions<Meta>
): EqualityComparator<Meta>;Usage Examples:
import { createCustomEqual, createCustomCircularEqual } from "fast-equals";
// Create custom comparator that ignores RegExp flags
const customEqual = createCustomEqual((defaultOptions) => ({
areRegExpsEqual: (a, b) => a.source === b.source, // Ignore flags
}));
const regex1 = /test/gi;
const regex2 = /test/m;
customEqual(regex1, regex2); // true (same source, flags ignored)
// Create custom circular comparator for legacy environments
const customCircularEqual = createCustomCircularEqual((defaultOptions) => ({
// Use custom cache implementation instead of WeakMap
createIsNestedEqual: (comparator) => {
const cache = new Map(); // Fallback for environments without WeakMap
return (a, b, indexOrKeyA, indexOrKeyB, parentA, parentB, meta) => {
// Custom circular detection logic
return comparator(a, b, meta);
};
},
}));/**
* Base interface for circular reference metadata
*/
interface BaseCircularMeta extends Pick<WeakMap<any, any>, 'delete' | 'get'> {
set(key: object, value: any): any;
}
/**
* Configuration options for comparator creators
*/
interface CreateComparatorCreatorOptions<Meta> {
areArraysEqual: TypeEqualityComparator<any, Meta>;
areDatesEqual: TypeEqualityComparator<any, Meta>;
areMapsEqual: TypeEqualityComparator<any, Meta>;
areObjectsEqual: TypeEqualityComparator<any, Meta>;
areRegExpsEqual: TypeEqualityComparator<any, Meta>;
areSetsEqual: TypeEqualityComparator<any, Meta>;
createIsNestedEqual: EqualityComparatorCreator<Meta>;
}
/**
* Function type for getting comparator options
*/
type GetComparatorOptions<Meta> = (
defaultOptions: CreateComparatorCreatorOptions<Meta>
) => Partial<CreateComparatorCreatorOptions<Meta>>;
/**
* Internal equality comparator used by the system
*/
type InternalEqualityComparator<Meta> = (
a: any,
b: any,
indexOrKeyA: any,
indexOrKeyB: any,
parentA: any,
parentB: any,
meta: Meta
) => boolean;
/**
* Conditional type for equality comparators based on Meta parameter
*/
type EqualityComparator<Meta> = Meta extends undefined
? <A, B>(a: A, b: B, meta?: Meta) => boolean
: <A, B>(a: A, b: B, meta: Meta) => boolean;
/**
* Creates internal equality comparator from external one
*/
type EqualityComparatorCreator<Meta> = (
fn: EqualityComparator<Meta>
) => InternalEqualityComparator<Meta>;
/**
* Simple equality comparator without metadata
*/
type NativeEqualityComparator = <A, B>(a: A, b: B) => boolean;
/**
* Typed equality comparator for specific types
*/
type TypeEqualityComparator<Type, Meta> = (
a: Type,
b: Type,
isEqual: InternalEqualityComparator<Meta>,
meta: Meta
) => boolean;