jest-extended
jest-extended copied to clipboard
TypeScript declarations are not working when used with @jest/globals
Hello!
Thank you for this great library!
However, when using TypeScript and Jest with @jest/globals, the declarations provided by the package doesn't work. E.g.:
import 'jest-extended';
import { expect } from '@jest/globals';
// TS2339: Property toIncludeSameMembers does not exist on type
expect([]).toIncludeSameMembers([]);
Have you considered this use case? Thank you.
To make the asymmetric matchers visible, you can do the following in your globals.d.ts or equivalent:
import * as jestExtended from 'jest-extended';
type JestExtended = typeof jestExtended;
declare module 'expect' {
interface AsymmetricMatchers extends JestExtended {}
}
However, this requires you to use asymmetric matching in all cases by using toEqual; e.g. for your original example:
expect([]).toEqual(expect.toIncludeSameMembers([]));
For regular matchers you can also kinda sorta do this:
declare module 'expect' {
interface Matchers extends JestExtended {}
}
However the problem is that the object exported by jest-extended is of type CustomMatchers<any>, which results in expect() returning an object that allows arbitrary members, compromising type safety. If the package exported the generic CustomMatchers interface itself, alleviating the need for typeof, we could do:
import {CustomMatchers} from 'jest-extended';
declare module 'expect' {
interface Matchers<R> extends CustomMatchers<R> {}
}
Of course, ideally, the package should just do this itself for both kinds of matchers.
Working with TS 5.4+
// global.d.ts
declare module 'expect' {
interface Matchers<R> extends CustomMatchers<R> {}
interface AsymmetricMatchers extends CustomMatchers<unknown> {}
}
export {};
To get typings working in my project I used the following, which also fixes the fixed any issue:
/* eslint-disable @typescript-eslint/no-explicit-any */
import "@jest/globals";
import JestExtendedMatchers from "jest-extended";
type ReplaceReturnType<T extends (...args: any) => any, TNewReturn> = (
...args: Parameters<T>
) => TNewReturn;
/**
* Jest matchers for Jest Extended.
* JestExtendedMatchers is fixed on type `any`.
* To make them work they need to be of type `R` (which is `void | Promise<void>`).
*/
type JestExtendedMatchersFixed<R> = {
[K in keyof typeof JestExtendedMatchers]: ReplaceReturnType<
(typeof JestExtendedMatchers)[K],
R
>;
};
declare global {
namespace jest {
interface AsymmetricMatchers extends JestExtendedMatchers<R> {}
interface Matchers<R> extends JestExtendedMatchers<R> {}
}
}
declare module "expect" {
interface AsymmetricMatchers extends JestExtendedMatchersFixed<R> {}
interface Matchers<R> extends JestExtendedMatchersFixed<R> {}
}
This adheres to the TypeScript example, as documented here: https://jestjs.io/docs/expect#expectextendmatchers