JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 220
  • Score
    100M100P100Q87547F
  • License MIT

A value equality utility library.

Package Exports

  • @a11d/equals
  • @a11d/equals/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@a11d/equals) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

@a11d/equals

Value equality for JavaScript. Compares objects, arrays, maps, sets, and functions by structure instead of reference.

import '@a11d/equals'
import { equals } from '@a11d/equals'

const obj1 = { a: 1, b: [1, 2, 3] }
const obj2 = { a: 1, b: [1, 2, 3] }

Object[equals](obj1, obj2) // true
obj1 === obj2 // false

Installation

npm install @a11d/equals

Usage

Two APIs available:

Symbol-based (recommended):

import { equals } from '@a11d/equals'
obj[equals](other)

Global methods (for .equals() syntax or when the symbol conflicts with other libraries):

import '@a11d/equals/global'
obj.equals(other)

Built-in types

Objects — Deep comparison of properties. Requires same constructor and all property values to be equal.

Objects with valueOf()

Compared by their primitive values:

const a = { valueOf: () => 1 }
const b = { valueOf: () => 1 }

Object[equals](a, b) // true

undefined vs absence

Missing properties and undefined values are treated as equal, but null is different:

const obj1 = { a: 1, b: undefined }
const obj2 = { a: 1 }
const obj3 = { a: 1, b: null }

Object[equals](obj1, obj2) // true
Object[equals](obj1, obj3) // false

Prototype-less objects

Objects created with Object.create(null) work fine:

const a = Object.create(null)
a.prop = 'value'

const b = { prop: 'value' }

Object[equals](a, b) // true
Arrays — Element-by-element comparison.

Nested structures are compared deeply:

const arr1 = [1, 2, { key: 'value' }]
const arr2 = [1, 2, { key: 'value' }]

Object[equals](arr1, arr2) // true
Maps & Sets — Entry/element comparison with deep equality.

Values and elements are compared deeply. Note that Map keys are compared by identity:

const map1 = new Map([['a', { id: 1 }], ['b', { id: 2 }]])
const map2 = new Map([['a', { id: 1 }], ['b', { id: 2 }]])

const set1 = new Set([1, { key: 'value' }])
const set2 = new Set([1, { key: 'value' }])

Object[equals](map1, map2) // true
Object[equals](set1, set2) // true
Functions — Functions with identical string representation are equal.
const fn1 = () => 42
const fn2 = () => 42

Object[equals](fn1, fn2) // true

Note: This compares the .toString() output, so functions with the same code but different names or closures may differ.

Custom equality

Implement the equals symbol (or .equals() method with global API):

import { equals } from '@a11d/equals'

class Person {
    constructor(public name: string, public age: number) {}

    [equals](other: unknown): boolean {
        return other instanceof Person
            && this.name === other.name
            && this.age === other.age
    }
}

Lit integration

Use hasChanged to trigger re-renders only on structural changes:

import { hasChanged } from '@a11d/equals'

class MyComponent extends Component {
    @property({ type: Object, hasChanged })
    data = { name: 'John', items: [1, 2, 3] }
}

Without this, Lit re-renders whenever you assign a new object reference, even if the content is identical.