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

  1. Implement `toAsymmetricMatcher()` on the custom matcher class returning the desired string representation (e.g. `StringContaining <sample>`).
  2. Ensure toString() returns one of the recognized built-in names if you intend to reuse built-in serialization (ArrayContaining, ObjectContaining, StringMatching, etc.).
  3. 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

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


AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03). Data as JSON: /data/errors/9094e629763e224e.json. Report an issue: GitHub.