Skip to content

feat: add matches and isMatch functions#383

Open
boy672820 wants to merge 3 commits intomarpple:mainfrom
boy672820:feat/matches
Open

feat: add matches and isMatch functions#383
boy672820 wants to merge 3 commits intomarpple:mainfrom
boy672820:feat/matches

Conversation

@boy672820
Copy link

Fixes #380

Summary

  • Add matches function - creates a predicate that checks if an input matches all properties in a pattern
  • Add isMatch function - performs partial deep comparison between two values

Features

  • Deep comparison for nested objects and arrays
  • Support for Date, RegExp, Map, Set
  • Works with filter, find, some, every

@boy672820 boy672820 requested a review from ppeeou as a code owner February 16, 2026 16:56
Copy link
Member

@ppeeou ppeeou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your interest in FxTS!

Here are a few comments after reviewing the code.


1. isMatch — Map/Set partial matching inconsistency

isMatch is documented as a "partial deep comparison", and objects do behave as partial matches:

isMatch({ a: 1, b: 2 }, { a: 1 }); // true — OK if source is a subset

However, Map and Set require an exact size match:

// src/isMatch.ts
if (source instanceof Map && object instanceof Map) {
    if (source.size !== object.size) return false;  // ← not partial

isMatch(new Map([['a',1],['b',2]]), new Map([['a',1]]))false, but the same data represented as a plain object would return true. Consider either applying partial matching semantics consistently, or explicitly documenting that Map/Set use exact match.


2. matches type signature — T always resolves to unknown

function matches<T>(pattern: Record<Key, any>): (input: T) => boolean;

T is never inferred from the pattern, so matches({ age: 30 }) always returns (input: unknown) => boolean. There is no type checking on pattern keys, meaning non-existent properties pass silently:

// No type error — age2 doesn't exist on User, but compiles fine
filter(matches({ age2: 30 }), users);

Consider leveraging Partial<T> or similar approaches to provide type safety for pattern keys.


3. isMatch Set comparison — greedy matching

When comparing Sets, each source value is searched against all object values, but already-matched object values can be matched again:

for (const value of source) {
  let found = false;
  for (const objValue of object) {  // ← already matched objValue can be matched again
    if (isMatch(objValue, value)) {
      found = true;
      break;
    }
  }
}

Currently the exact size check mitigates this, but if issue #1 is addressed by switching to partial matching, this would become a bug. Consider tracking matched values (e.g., using a matched Set).


4. entries only returns string keys

The pattern type for matches is Record<Key, any> where Key = string | symbol | number, but entries(pattern) is based on Object.entries, which ignores symbol keys.

const sym = Symbol('id');
matches({ [sym]: 123 }); // symbol key is silently ignored during comparison

Since the Key type includes symbol, consider either documenting this limitation or excluding symbol from the accepted key type.

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.

feat: add matches function for object pattern matching

2 participants

Comments