- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 6.6k
Description
Disclaimer: I'm trying out a bit more AI as we speak, and as we can clearly see below 😅
Page(s)
https://jestjs.io/docs/expect#expectextendmatchers
declare module 'expect' {
  interface AsymmetricMatchers {
    toBeWithinRange(floor: number, ceiling: number): void;
  }
  interface Matchers<R> {
    toBeWithinRange(floor: number, ceiling: number): R;
  }
}tip
The type declaration of the matcher can live in a .d.ts file or in an imported .ts module (see JS and TS examples above respectively). If you keep the declaration in a .d.ts file, make sure that it is included in the program and that it is a valid module, i.e. it has at least an empty export {}.
Description
🔬 Minimal Reproduction
Repository: https://github.com/dprevost-LMI/jest-extend-repro
Setup:
// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  injectGlobals: true, // ← This is key
  setupFilesAfterEnv: ['<rootDir>/jest.setup.ts']
};// jest.setup.ts
import type {MatcherFunction} from 'expect';
const toBeWithinRange: MatcherFunction<[floor: unknown, ceiling: unknown]> =
  function (actual, floor, ceiling) {
    // Implementation...
    const pass = actual >= floor && actual <= ceiling;
    return { pass, message: () => `...` };
  };
expect.extend({
  toBeWithinRange,
});❌ Current Documentation Approach is generating confusion:
// types/jest-extensions.d.ts
declare module 'expect' {
  interface AsymmetricMatchers {
    toBeWithinRange(floor: number, ceiling: number): void;
  }
  interface Matchers<R> {
    toBeWithinRange(floor: number, ceiling: number): R;
  }
}Result: TypeScript errors:
Property 'toBeWithinRange' does not exist on type 'InverseAsymmetricMatchers'.
Property 'toBeWithinRange' does not exist on type 'JestMatchers'.
✅ Working Solution:
// types/jest-extensions.d.ts
declare namespace jest {
  interface Matchers<R> {
    toBeWithinRange(floor: number, ceiling: number): R;
  }
  
  interface Expect {
    toBeWithinRange(floor: number, ceiling: number): any;
  }
  
  interface InverseAsymmetricMatchers {
    toBeWithinRange(floor: number, ceiling: number): any;
  }
}🧐 Expected Behavior
The documentation should clearly explain:
- When to use declare module 'expect'vsdeclare namespace jest
- How injectGlobals: trueaffects type declarations
- Complete interface requirements for asymmetric matchers
📝 Current Documentation Issues
1. Missing InverseAsymmetricMatchers interface
The docs show AsymmetricMatchers but not InverseAsymmetricMatchers, which is required for expect.not.customMatcher().
2. Inconsistent module declaration approach
- Shows declare module 'expect'but doesn't explain when this works vs when to usedeclare namespace jest
- Doesn't mention that injectGlobals: truechanges the type requirements
3. Incomplete TypeScript examples
- Missing the Expectinterface for asymmetric usage
- No guidance on .d.tsfile setup vs inline declarations
🌍 Environment
- Jest version: 30.0.3
- TypeScript version: 5.8.3
- ts-jest version: 29.4.0
- Configuration: injectGlobals: true, external.d.tsfiles
📚 Suggested Documentation Improvements
Add a section: "TypeScript Declarations for Different Setups"
Setup 1: Import-based approach with @jest/globals
import {expect} from '@jest/globals';
declare module 'expect' {
  interface AsymmetricMatchers {
    toBeWithinRange(floor: number, ceiling: number): void;
  }
  interface Matchers<R> {
    toBeWithinRange(floor: number, ceiling: number): R;
  }
}Setup 2: Global Jest with external .d.ts files
// types/jest-extensions.d.ts
declare namespace jest {
  interface Matchers<R> {
    toBeWithinRange(floor: number, ceiling: number): R;
  }
  
  interface Expect {
    toBeWithinRange(floor: number, ceiling: number): any;
  }
  
  interface InverseAsymmetricMatchers {
    toBeWithinRange(floor: number, ceiling: number): any;
  }
}Setup 3: Inline global declarations
declare global {
  namespace jest {
    interface Matchers<R> {
      toBeWithinRange(floor: number, ceiling: number): R;
    }
    // ... other interfaces
  }
}🎯 Affected Documentation Pages
- Expect - Custom Matchers
- Jest Configuration - setupFilesAfterEnv
- TypeScript usage examples throughout the docs
💡 Additional Context
This issue particularly affects developers who:
- Use injectGlobals: true(the default behavior)
- Organize TypeScript declarations in separate .d.tsfiles
- Use asymmetric matchers in object matching scenarios
- Follow modern Jest + TypeScript patterns
The current documentation leads to a confusing trial-and-error experience when the declare module 'expect' approach fails silently or with cryptic TypeScript errors.