jestjs/jest · error · TypeError
expect.extend: `${key}` is not a valid matcher. Must be a fu
Error message
expect.extend: `${key}` is not a valid matcher. Must be a function, is "${getType(matcher)}" What it means
setMatchers (called by expect.extend) iterates each key and verifies `typeof matcher === 'function'`; a non-function value throws TypeError with the actual type. Jest builds an asymmetric matcher variant and registers the matcher for dispatch, both of which require the value to be callable.
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 f49721c78e)
Solutions
- Make every value in the object a function with signature (this: MatcherContext, received, ...args) => result.
- Verify each import resolves to a function (check named/default export names).
- If you intended a namespace, pass each function individually: expect.extend({ foo: ns.foo }).
Example fix
// before — default import mistake or placeholder
import matchers from './matchers'; // undefined default export
expect.extend({ toBeFoo: matchers }); // matchers is undefined
// after
import { toBeFoo } from './matchers';
expect.extend({ toBeFoo }); Defensive patterns
Strategy: type-guard
Validate before calling
for (const [k, v] of Object.entries(matchers)) {
if (typeof v !== 'function') {
throw new TypeError(`expect.extend: ${k} is not a function`);
}
}
expect.extend(matchers); Type guard
function isMatchersObject(x: Record<string, unknown>): x is Record<string, (...args: unknown[]) => unknown> {
return Object.values(x).every(v => typeof v === 'function');
} Try / catch
// setMatchers throws synchronously during expect.extend — validate the input map first
Prevention
- Type expect.extend's argument as Record<string, MatcherFunction>.
- Verify named imports exist before bundling them into extend.
- Use a typed helper that rejects non-function values at the boundary.
When it happens
Trigger: Calling expect.extend({ foo: 42 }), expect.extend({ foo: null }), expect.extend({ foo: 'some string' }), expect.extend({ foo: { bar() {} } }) (object instead of the function), or importing a matcher from a module that did not export it (undefined).
Common situations: Wrong/named import (importing a non-existent export gives undefined); passing a config object instead of the matcher function; copy-paste leaving a placeholder; spreading a partial object whose values aren't functions.
Related errors
- Unexpected return from a matcher function. Matcher functions
- 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
- expect.customEqualityTesters: Must be set to an array of Tes
AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03).
Data as JSON: /data/errors/fff84e3eaa23c900.json.
Report an issue: GitHub.