Skip to content

fix: consider extra own-enumerable properties on arrays, not just indices - #112

Open
rajanpanth wants to merge 1 commit into
chaijs:mainfrom
rajanpanth:fix/array-extra-properties
Open

fix: consider extra own-enumerable properties on arrays, not just indices#112
rajanpanth wants to merge 1 commit into
chaijs:mainfrom
rajanpanth:fix/array-extra-properties

Conversation

@rajanpanth

Copy link
Copy Markdown

Summary

Array (along with Arguments/TypedArrays) dispatches to iterableEqual, which only compares length and indexed values — so a non-index own-enumerable property added to an array is silently ignored:

var a = [1, 2, 3]; a.extra = 'x';
var b = [1, 2, 3]; b.extra = 'y';
deepEql(a, b); // true -- but the README says otherwise

This contradicts the library's own documented rule (README, "Rules" section): "All own and inherited enumerable properties are considered."

Changes

  • index.js: added arrayExtraKeysEqual, called after iterableEqual for the 'Array' case specifically. It collects own+inherited enumerable keys/symbols (via the existing getEnumerableKeys/getEnumerableSymbols helpers, matching objectEqual's own approach), filters out keys that are already-covered array indices (so holes/sparse-array semantics are untouched — those still go through iterableEqual exactly as before), and compares whatever's left via the existing keysEqual.
  • Scoped to Array only, not Arguments/TypedArrays — they share the same dispatch branch and likely the same gap, but I've only verified the Array case, so I didn't want to guess at behavior for the others (e.g. arguments.length is enumerable, unlike array's, which needs its own look).
  • test/index.js: one new case confirming extra properties are compared, and that plain arrays are unaffected.

Testing

  • Full suite: 175/175 passing (up from 174).
  • Confirmed the new test fails on the prior code and passes with the fix.
  • Manually checked this doesn't regress holes/sparse-array behavior (eql(new Array(3), new Array(3)) and eql([1,,3], [1, undefined, 3]) both still return their existing values) or symbol-keyed extras (distinct symbols with the same description still compare as different keys, matching objectEqual's existing symbol handling).
  • eslint clean, aside from the same pre-existing linebreak-style (Windows checkout CRLF, normalizes to LF on commit) findings present with or without this change.

…ices

Array/Arguments/TypedArray all dispatched to iterableEqual, which only
compares length and indexed values -- so a non-index own-enumerable
property added to an array (e.g. arr.extra = 'x') was silently ignored,
contradicting this library's own documented rule that all own and
inherited enumerable properties are considered.

Scoped this to Array specifically, since that's the case I verified;
Arguments/TypedArrays share the same dispatch branch and likely the
same gap, but I haven't tested those, so left them as-is rather than
guess.
@rajanpanth

Copy link
Copy Markdown
Author

Friendly follow-up on this PR 👋

When you have a moment, could you please take a look? I’m happy to update anything if needed.

Thanks!

@43081j

43081j commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

presumably this and #111 are trying to solve the same problem.

i haven't reviewed either yet as this isn't necessarily a bug, it could also just be a miss in documentation. we need to decide if our intent is to treat arrays as non-extensible objects or treat them like any other object (the latter is what would need this as a "fix").

@rajanpanth

Copy link
Copy Markdown
Author

Good point — agreed this hinges on intended array semantics, not just mechanics. My intent in this PR is the ‘arrays are still objects’ direction (so own enumerable props should participate in equality), and the tests are written with that model in mind.\n\nIf maintainers prefer arrays to be treated as index-only/non-extensible for equality, I’m happy to pivot this from a behavior fix into a docs clarification instead.

@keithamus

Copy link
Copy Markdown
Member

It might be worth looking at the history here. I seem to recall we’ve flip-flopped on this a lot.

@rajanpanth

Copy link
Copy Markdown
Author

I traced the history. Before 1.0, arrays went through objectEqual, so their enumerable keys (including non-index properties) participated in equality. Commit 5d6124b (the 1.0 refactor) introduced the dedicated ArrayiterableEqual dispatch; that helper only walks length and numeric indices. I found no later commit documenting an array exception.

The subsequent history points the other way: 022035a restored own/inherited-enumerable comparison after the same refactor, and b3c3ded then documented “All own and inherited enumerable properties are considered.” #111 exposes the same special-type early-return gap for Date/RegExp/boxed primitives; this PR stays Array-only to avoid overlapping that contributor’s work.

So my read is that the current behavior is an accidental 1.0 regression rather than an intentional index-only array contract. If you prefer to treat arrays as a documented exception instead, I can close this in favor of a docs change, but the pre-1.0 behavior and post-1.0 rule both support the current patch.

@43081j 43081j self-assigned this Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants