jestjs/jest · error · TypeError
Asymmetric matcher ${val.constructor.name} does not implemen
Error message
Asymmetric matcher ${val.constructor.name} does not implement toAsymmetricMatcher() What it means
The AsymmetricMatcher plugin serializes any object whose $$typeof matches Symbol.for('jest.asymmetricMatcher'). After handling Jest's built-in matchers (ArrayContaining, ObjectContaining, StringMatching, StringContaining, ArrayOf, etc.) by their toString() value, the fallback at AsymmetricMatcher.ts:96 requires the object to implement toAsymmetricMatcher(). A custom matcher tagged with the asymmetricMatcher symbol but missing that method throws this TypeError.
Source
Thrown at packages/pretty-format/src/plugins/AsymmetricMatcher.ts:97
printer(val.sample, config, indentation, depth, refs)
);
}
if (stringedValue === 'ArrayOf' || stringedValue === 'NotArrayOf') {
if (++depth > config.maxDepth) {
return `[${stringedValue}]`;
}
return `${stringedValue + SPACE}${printer(
val.sample,
config,
indentation,
depth,
refs,
)}`;
}
if (typeof val.toAsymmetricMatcher !== 'function') {
throw new TypeError(
`Asymmetric matcher ${val.constructor.name} does not implement toAsymmetricMatcher()`,
);
}
return val.toAsymmetricMatcher();
};
export const test: NewPlugin['test'] = (val: any) =>
val && val.$$typeof === asymmetricMatcher;
const plugin: NewPlugin = {serialize, test};
export default plugin;
View on GitHub (pinned to f49721c78e)
Solutions
- Implement `toAsymmetricMatcher()` on the custom matcher class returning the desired string representation (e.g. `StringContaining <sample>`).
- Ensure toString() returns one of the recognized built-in names if you intend to reuse built-in serialization (ArrayContaining, ObjectContaining, StringMatching, etc.).
- Do not set $$typeof to the jest.asymmetricMatcher symbol unless you fully implement the matcher contract.
Example fix
// before
class MyMatcher {
constructor(sample) { this.sample = sample; }
$$typeof = Symbol.for('jest.asymmetricMatcher');
asymmetricMatch(other) { return other === this.sample; }
}
// after
class MyMatcher {
constructor(sample) { this.sample = sample; }
$$typeof = Symbol.for('jest.asymmetricMatcher');
asymmetricMatch(other) { return other === this.sample; }
toAsymmetricMatcher() { return `MyMatcher ${this.sample}`; }
} Defensive patterns
Strategy: type-guard
Validate before calling
function assertMatcherSerializable(matcher) {
if (matcher && matcher.$$typeof === Symbol.for('jest.asymmetricMatcher')) {
if (typeof matcher.toAsymmetricMatcher !== 'function' &&
!['ArrayContaining','ArrayNotContaining','ObjectContaining','ObjectNotContaining','StringMatching','StringNotMatching','StringContaining','StringNotContaining','ArrayOf','NotArrayOf'].includes(matcher.toString())) {
throw new TypeError(`${matcher.constructor.name} must implement toAsymmetricMatcher()`);
}
}
} Type guard
function isSerializableAsymmetricMatcher(val): boolean {
if (!val || val.$$typeof !== Symbol.for('jest.asymmetricMatcher')) return false;
return typeof val.toAsymmetricMatcher === 'function' ||
/^(ArrayContaining|ArrayNotContaining|ObjectContaining|ObjectNotContaining|StringMatching|StringNotMatching|StringContaining|StringNotContaining|ArrayOf|NotArrayOf)$/.test(val.toString());
} Prevention
- Implement toAsymmetricMatcher() on every custom asymmetric matcher you tag with the jest.asymmetricMatcher symbol.
- Avoid setting $$typeof to Symbol.for('jest.asymmetricMatcher') unless you implement the full matcher contract.
- After upgrading pretty-format/jest, re-test custom matchers since the built-in toString() branch list can change.
When it happens
Trigger: Creating a custom asymmetric matcher that sets `this.$$typeof = Symbol.for('jest.asymmetricMatcher')` (or extends a base that does) without implementing a `toAsymmetricMatcher()` method, then letting pretty-format snapshot/serialize it (e.g. via expect.extend with an improperly formed matcher, or expect.any-like helpers).
Common situations: Library authors building custom expect matchers, upgrading jest/pretty-format across versions where the matcher contract changed, or copy-pasting a matcher that relies on toString() names that no longer match the built-in branches so execution falls through to the toAsymmetricMatcher() requirement.
Related errors
- babel-jest: Babel ignores ${slash(path.relative(cwd, filenam
- any() expects to be passed a constructor function. Please pa
- You must provide an array to ${this.toString()}, not '${type
- You must provide an object to ${this.toString()}, not '${typ
- Expected is not a string
AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03).
Data as JSON: /data/errors/9094e629763e224e.json.
Report an issue: GitHub.