jestjs/jest · error · TypeError
expect.extend: ` ` is not a valid matcher. Must be a…
Error message
expect.extend: `${key}` is not a valid matcher. Must be a function, is "${getType(matcher)}" What it means
expect.extend(matchers) registers custom matchers; setMatchers iterates the keys and requires each value to be a function (the matcher implementation). If any value is not a function (e.g. an object, a boolean, or a config descriptor), it throws a TypeError naming the offending key and its actual type. This prevents registering non-callable 'matchers' that would fail later with a less clear error.
Solutions
- Ensure every value in the object passed to expect.extend is a function with the matcher signature.
- If your matchers are exported as named functions, import and pass them directly: `expect.extend({ toBeFoo })`.
- If a module exports matchers under a key (e.g. `matchers` property), spread only that sub-object, not the whole module namespace.
Example fix
// before
import * as matchers from './my-matchers';
expect.extend(matchers); // module namespace has non-function members
// after
import { toBeFoo, toBeBar } from './my-matchers';
expect.extend({ toBeFoo, toBeBar }); Defensive patterns
Strategy: type-guard
Validate before calling
function extendSafe(obj) {
for (const [key, value] of Object.entries(obj)) {
if (typeof value !== 'function') {
throw new TypeError(`expect.extend: '${key}' is not a function (got ${typeof value})`);
}
}
expect.extend(obj);
} Type guard
function isMatchersObject(v: Record<string, unknown>): boolean {
return Object.values(v).every(x => typeof x === 'function');
}
if (!isMatchersObject(matchers)) {
throw new TypeError('All expect.extend values must be functions');
}
expect.extend(matchers); Prevention
- Import named matcher functions rather than whole module namespaces.
- Spread only the sub-object that contains the matcher functions.
- Add a unit test that registers matchers in isolation to catch non-function entries.
When it happens
Trigger: Calling `expect.extend({ name: value })` where value is not a function — e.g. passing a plain object, an array, a default-export module object, or a boolean flag. Also when spreading a module's exports that include non-function members.
Common situations: Importing a matcher module's default object but passing the namespace instead of the function; copying a matcher config that includes options as top-level values; a typo where a config object is passed in place of the matcher function.
Related errors
- Unexpected return from a matcher function. Matcher…
- any() expects to be passed a constructor function. Please…
- expect.customEqualityTesters: Must be set to an array of…
- Expected is not a Number
- Expected is not a string
AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10).
Data as JSON: /api/errors/fff84e3eaa23c900.
Report an issue: GitHub.
Appendix: source
Thrown at packages/expect/src/jestMatchersObject.ts:65
export const setState = <State extends MatcherState = MatcherState>(
state: Partial<State>,
): void => {
Object.assign((globalThis as any)[JEST_MATCHERS_OBJECT].state, state);
};
export const getMatchers = (): MatchersObject =>
(globalThis as any)[JEST_MATCHERS_OBJECT].matchers;
export const setMatchers = (
matchers: MatchersObject,
isInternal: boolean,
expect: Expect,
): void => {
for (const key of Object.keys(matchers)) {
const matcher = matchers[key];
if (typeof matcher !== 'function') {
throw new TypeError(
`expect.extend: \`${key}\` is not a valid matcher. Must be a function, is "${getType(
matcher,
)}"`,
);
}
Object.defineProperty(matcher, INTERNAL_MATCHER_FLAG, {
value: isInternal,
});
if (!isInternal) {
// expect is defined
class CustomMatcher extends AsymmetricMatcher<
[unknown, ...Array<unknown>]
> {
constructor(inverse = false, ...sample: [unknown, ...Array<unknown>]) {
super(sample, inverse);View on GitHub (pinned to 8e6d128e4a)