Extending third party module that is globally exposed

后端 未结 3 1393
-上瘾入骨i
-上瘾入骨i 2020-12-05 17:57

I am trying to add a custom matcher to Jest in Typescript. This works fine, but I can\'t get Typescript to recognize the extended Matchers.

myMatcher.ts

相关标签:
3条回答
  • 2020-12-05 18:26

    OK, so there are a few issues here

    When a source file (.ts or .tsx) file and a declaration file (.d.ts) file are both candidates for module resolution, as is the case here, the compiler will resolve the source file.

    You probably have two files because you want to export a value and also modify the type of the global object jest. However, you do not need two files for this as TypeScript has a specific construct for augmenting the global scope from within a module. That is to say, all you need is the following .ts file

    myMatcher.ts

    // use declare global within a module to introduce or augment a global declaration.
    declare global {
      namespace jest {
        interface Matchers {
          myMatcher: typeof myMatcher;
        }
      }
    }
    export default function myMatcher<T>(this: jest.MatcherUtils, received: T, expected: T) {
      const pass = received === expected;
      return {
        pass,
        message: () => `expected ${pass ? '!' : '='}==`
      };
    }
    

    That said, if you have such a situation, it is a good practice to perform the global mutation and the global type augmentation in the same file. Given that, I would consider rewriting it as follows

    myMatcher.ts

    // ensure this is parsed as a module.
    export {};
    
    declare global {
      namespace jest {
        interface Matchers {
          myMatcher: typeof myMatcher;
        }
      }
    }
    function myMatcher<T>(this: jest.MatcherUtils, received: T, expected: T) {
      const pass = received === expected;
      return {
        pass,
        message: () => `expected ${pass ? '!' : '='}==`
      };
    }
    
    expect.extend({
      myMatcher
    });
    

    someTest.ts

    import './myMatcher';
    
    it('should work', () => {
      expect('str').myMatcher('str');
    });
    
    0 讨论(0)
  • 2020-12-05 18:38

    Answer by @AluanHaddad is almost correct, sans a few types. This one works:

    export {};
    
    declare global {
      namespace jest {
        interface Matchers<R> {
          myMatcher: (received: string) => R;
        }
      }
    }
    
    function myMatcher<T>(this: jest.MatcherUtils, received: string, expected: string): jest.CustomMatcherResult {
      const pass = received === expected;
      return {
        pass,
        message: (): string => `expected ${received} to be ${expected}`,
      }
    }
    
    expect.extend({
      myMatcher,
    });
    

    For a real-world example, see https://github.com/Quantum-Game/quantum-tensors/blob/master/tests/customMatchers.ts (and that the tests actually pass: https://travis-ci.com/Quantum-Game/quantum-tensors).

    0 讨论(0)
  • 2020-12-05 18:41

    A simple way is:

    customMatchers.ts

    declare global {
        namespace jest {
            interface Matchers<R> {
                // add any of your custom matchers here
                toBeDivisibleBy: (argument: number) => {};
            }
        }
    }
    
    // this will extend the expect with a custom matcher
    expect.extend({
        toBeDivisibleBy(received: number, argument: number) {
            const pass = received % argument === 0;
            if (pass) {
                return {
                    message: () => `expected ${received} not to be divisible by ${argument}`,
                    pass: true
                };
            } else {
                return {
                    message: () => `expected ${received} to be divisible by ${argument}`,
                    pass: false
                };
            }
        }
    });
    

    my.spec.ts

    import "path/to/customMatchers";
    
    test('even and odd numbers', () => {
       expect(100).toBeDivisibleBy(2);
       expect(101).not.toBeDivisibleBy(2);
    });
    
    0 讨论(0)
提交回复
热议问题