{"record":{"id":"fff84e3eaa23c900","repo":"jestjs/jest","slug":"expect-extend-key-is-not-a-valid-matcher-mu","errorCode":null,"errorMessage":"expect.extend: `${key}` is not a valid matcher. Must be a function, is \"${getType(matcher)}\"","messagePattern":"expect\\.extend: `(.+?)` is not a valid matcher\\. Must be a function, is \"(.+?)\"","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/expect/src/jestMatchersObject.ts","lineNumber":65,"sourceCode":"export const setState = <State extends MatcherState = MatcherState>(\n  state: Partial<State>,\n): void => {\n  Object.assign((globalThis as any)[JEST_MATCHERS_OBJECT].state, state);\n};\n\nexport const getMatchers = (): MatchersObject =>\n  (globalThis as any)[JEST_MATCHERS_OBJECT].matchers;\n\nexport const setMatchers = (\n  matchers: MatchersObject,\n  isInternal: boolean,\n  expect: Expect,\n): void => {\n  for (const key of Object.keys(matchers)) {\n    const matcher = matchers[key];\n\n    if (typeof matcher !== 'function') {\n      throw new TypeError(\n        `expect.extend: \\`${key}\\` is not a valid matcher. Must be a function, is \"${getType(\n          matcher,\n        )}\"`,\n      );\n    }\n\n    Object.defineProperty(matcher, INTERNAL_MATCHER_FLAG, {\n      value: isInternal,\n    });\n\n    if (!isInternal) {\n      // expect is defined\n\n      class CustomMatcher extends AsymmetricMatcher<\n        [unknown, ...Array<unknown>]\n      > {\n        constructor(inverse = false, ...sample: [unknown, ...Array<unknown>]) {\n          super(sample, inverse);","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/jestjs/jest/blob/8e6d128e4a278059ecddecaa97400b04c8ae5fd9/packages/expect/src/jestMatchersObject.ts#L47-L83","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nimport * as matchers from './my-matchers';\nexpect.extend(matchers); // module namespace has non-function members\n\n// after\nimport { toBeFoo, toBeBar } from './my-matchers';\nexpect.extend({ toBeFoo, toBeBar });","handlingStrategy":"type-guard","validationCode":"function extendSafe(obj) {\n  for (const [key, value] of Object.entries(obj)) {\n    if (typeof value !== 'function') {\n      throw new TypeError(`expect.extend: '${key}' is not a function (got ${typeof value})`);\n    }\n  }\n  expect.extend(obj);\n}","typeGuard":"function isMatchersObject(v: Record<string, unknown>): boolean {\n  return Object.values(v).every(x => typeof x === 'function');\n}\n\nif (!isMatchersObject(matchers)) {\n  throw new TypeError('All expect.extend values must be functions');\n}\nexpect.extend(matchers);","tryCatchPattern":null,"preventionTips":["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."],"tags":["expect","expect-extend","custom-matcher","typeerror"],"backgroundTag":null,"analyzedSha":"8e6d128e4a278059ecddecaa97400b04c8ae5fd9","analyzedAt":"2026-08-10T18:11:27.960Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}