如何让 TypeScript 知道自定义 Jest 匹配器?

Posted

技术标签:

【中文标题】如何让 TypeScript 知道自定义 Jest 匹配器?【英文标题】:How to let TypeScript know about custom Jest matchers? 【发布时间】:2020-04-17 02:10:38 【问题描述】:

我有一个使用 jest 的 react/typescript 项目,其中我有一个自定义匹配器,例如:

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 ;
            
        ;
    
;

我在测试中通过在 describe 函数中引用它:

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

并在it 函数中像这样使用:

expect(localValue).toBeTheSameAsRemote(remoteValue);

测试运行正常,但 typescript 编译器无法识别匹配器,这是有道理的,因为我没有在类型系统的任何地方定义它

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

到目前为止,我发现与扩展 jasmine 和/或 jest 的命名空间有关,例如

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

这对我没有用。

你有什么想法吗?

【问题讨论】:

【参考方案1】:

试试这个:

以下文件声明了实际的 实现 expect.extend 以及 TypeScript 声明。

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;

现在,在您的测试文件中,导入上述模块。

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);
   );
);

注意事项:

expect.extend 将从导入中自动调用。无需对每个测试文件都调用expect.extenddeclare global 是必需的,因为 jest 没有显式导入(它是全局导入)。 toBeTheSameAsRemote 函数的签名在 expect.extend 内部和 TypeScript 上不同。

【讨论】:

在 Typescript v4 中 @typescript-eslint/no-namespace 失败并显示 ES2015 module syntax is preferred over custom TypeScript modules and namespaces。有没有比简单地禁用此规则更好的方法?或者只要jest 是全局的,就必须禁用? ESLint:ES2015 模块语法优于自定义 TypeScript 模块和命名空间。(@typescript-eslint/no-namespace)

以上是关于如何让 TypeScript 知道自定义 Jest 匹配器?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Jest 的自定义测试环境文件中使用 TypeScript?

调度自定义事件并测试它是不是被正确触发(React TypeScript,Jest)

如何让 Jest 自定义匹配器在打字稿中工作?

使用 Typescript 从 Jest 手动模拟中导入函数

使用 Jest 在 VueJS 组件中模拟自定义模块

如何让 TypeScript 引擎在 JSX 中允许自定义 HTML 属性?