Skip to content

fix: object patterns no longer match array values#353

Open
chatman-media wants to merge 1 commit into
gvergnaud:mainfrom
chatman-media:fix/array-matches-empty-object-pattern
Open

fix: object patterns no longer match array values#353
chatman-media wants to merge 1 commit into
gvergnaud:mainfrom
chatman-media:fix/array-matches-empty-object-pattern

Conversation

@chatman-media

Copy link
Copy Markdown

Problem

Arrays are objects in JavaScript, which means typeof [] === 'object'. Because of this, when matching an array value against an object pattern like {}, the runtime matcher entered the object-pattern branch and called Reflect.ownKeys({}).every(…). Since every on an empty iterable always returns true, any array — including [] — matched the empty object pattern {}.

match([]).with({}, () => 'matched').otherwise(() => 'not matched')
// Expected: 'not matched'
// Actual:   'matched'  ← bug

Reported in #309.

Root cause

In src/internals/helpers.ts, matchPattern checks isObject(pattern) first, which is true for plain objects. Inside that block it checks Array.isArray(pattern) to handle tuple patterns, but it did not check whether value is an array before falling through to the plain-object key iteration:

// Before fix — no guard against array values here:
return Reflect.ownKeys(pattern).every((k) => );

Fix

Add a single Array.isArray(value) guard immediately before the Reflect.ownKeys iteration so that a non-array pattern is rejected when the value happens to be an array:

// After fix:
if (Array.isArray(value)) return false;
return Reflect.ownKeys(pattern).every((k) => );

Test evidence

New test in tests/objects.test.ts under issue #309:

case before fix after fix
match<any>([]).with({}, …) 'matched object pattern' 'no match'
match<any>([1,2,3]).with({}, …) 'matched object pattern' 'no match'
match<any>([1,2,3]).with({ length: 3 }, …) 'matched object pattern' 'no match'
match<any>({}).with({}, …) 'matched' (correct) 'matched' (still correct)
match<any>({ a:1 }).with({ a:1 }, …) 'matched' (correct) 'matched' (still correct)

Full suite: 454/454 tests pass after the fix.

Closes #309

Arrays are objects in JavaScript, so `{}` would vacuously match any
array (including `[]`) because `Reflect.ownKeys({}).every(…)` returns
`true` for an empty key set.

Add an early `Array.isArray(value)` guard in `matchPattern` so that
a plain-object pattern is rejected when the value is an array.

Closes gvergnaud#309
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.

empty array matches {} pattern

1 participant