How to let TypeScript know about custom Jest matchers?

6k views Asked by At

I have a react/typescript project, using jest, where I have a custom matcher like:

export const MyCustomMatchers = {
    toBeTheSameAsRemote: function(_util: any, _customEqualityTesters: any) {
        return {
            compare: function(actual: Brand, expected: RemoteBrand) {
                const pass: boolean = attributesMatch(actual, expected);
                const message: string = pass
                    ? 'Local matches Remote'
                    : 'Local does not match Remote';

                return { pass, message: () => message };
            }
        };
    }
};

which I reference in my tests by doing inside the describe function:

beforeEach(() => {
  jasmine.addMatchers(MyCustomMatchers);
});

And use like this in it functions:

expect(localValue).toBeTheSameAsRemote(remoteValue);

Tests run properly, but typescript compiler does not recognize the matcher, which makes sense cuz I haven't defined it anywhere in the types system

Property 'toBeTheSameAsRemote' does not exist on type 'JestMatchersShape<Matchers<void, MyType[]>, Matchers<Promise<void>, MyType[]>>'.ts(2339)

What I have found so far relates to extending the namespace for jasmine and/or jest, e.g.

declare namespace jasmine {
    interface Matchers {
        toBeTheSameAsRemote(remote: any): any;
    }
}

which hasn't worked for me.

Do you have any idea?

1

There are 1 answers

2
Andre Pena On

Try this:

The following file declares both the actual implementation expect.extend and also the TypeScript declaration.

custom-matcher.ts:

declare global {
  namespace jest {
    interface Matchers<R> {
        toBeTheSameAsRemote: (expected: string) => CustomMatcherResult;
    }
  }
}

expect.extend({
    /**
     * Notice that this implementation has 2 arguments, but the implementation inside the Matchers only has 1
     */
    toBeTheSameAsRemote(
    received: string,
    expected: string
  ) {
    return {
      pass: false,
      message: "A GraphQl error was expected"
    };
  }
});

// I am exporting nothing just so we can import this file
export default undefined;

Now, on your test file, import the above module.

actual-test.ts:

// importing the custom matcher file is important
import "../../testing/custom-matchers/custom-matcher";

describe("something", () => {
   it("should work", () => {
       expect(localValue).toBeTheSameAsRemote(remoteValue);
   });
});

Notes:

  • expect.extend will be called automatically from the import. There is no need to call expect.extend on every test file.
  • declare global is necessary because jest is not explicitly imported (it is a global import).
  • The signature of the toBeTheSameAsRemote function is not the same inside expect.extend and on TypeScript.