feat: add matches and isMatch functions#383
Conversation
ppeeou
left a comment
There was a problem hiding this comment.
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 subsetHowever, 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 partialisMatch(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 comparisonSince the Key type includes symbol, consider either documenting this limitation or excluding symbol from the accepted key type.
Fixes #380
Summary
matchesfunction - creates a predicate that checks if an input matches all properties in a patternisMatchfunction - performs partial deep comparison between two valuesFeatures
filter,find,some,every