扩展全球公开的第三方模块
Posted
技术标签:
【中文标题】扩展全球公开的第三方模块【英文标题】:Extending third party module that is globally exposed 【发布时间】:2017-09-25 19:09:01 【问题描述】:我正在尝试在 Typescript 中向 Jest 添加自定义匹配器。这很好用,但我无法让 Typescript 识别扩展的 Matchers
。
myMatcher.ts
export default function myMatcher (this: jest.MatcherUtils, received: any, expected: any): pass: boolean; message (): string;
const pass = received === expected;
return
pass: pass,
message: () => `expected $pass ? '!' : '='==`,
myMatcher.d.ts
declare namespace jest
interface Matchers
myMatcher (expected: any): boolean;
someTest.ts
import myMatcher from './myMatcher';
expect.extend(
myMatcher,
)
it('should work', () =>
expect('str').myMatcher('str');
)
tsconfig.json
"compilerOptions":
"outDir": "./dist/",
"moduleResolution": "node",
"module": "es6",
"target": "es5",
"lib": [
"es7",
"dom"
]
,
"types": [
"jest"
],
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist",
"doc",
"**/__mocks__/*",
"**/__tests__/*"
]
在 someTests.ts 中,我得到了错误
error TS2339: Property 'myMatcher' does not exist on type 'Matchers'
我已多次阅读 Microsoft 文档,但不知道如何将命名空间与全局可用类型(未导出)合并。
将它从 jest 放入 index.d.ts 效果很好,但对于快速变化的代码库和多方扩展的类来说,这不是一个好的解决方案。
【问题讨论】:
【参考方案1】:好的,这里有一些问题
当源文件(.ts
或 .tsx
)文件和声明文件(.d.ts
)文件都是模块解析的候选者时,如这里的情况,编译器将解析源文件。
您可能有两个文件,因为您想导出一个值并修改全局对象jest
的类型。但是,您不需要两个文件,因为 TypeScript 有一个特定的结构,用于从模块内扩大全局范围。也就是说,你只需要以下.ts
文件
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 ? '!' : '='==`
;
也就是说,如果您遇到这种情况,最好在同一个文件中执行全局mutation 和全局类型augmentation。鉴于此,我会考虑重写如下
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');
);
【讨论】:
在这里我认为可以将类型声明从 .ts 文件移到 .d.ts 文件中以进行组织。但实际上,.d.ts 文件只是纯 javascript 的 TS 外观。那里的普通进口也很好。有趣的是,如果我将声明语句放在 someTest.ts 文件中,我会丢失其余的 Matchers 声明信息。所以 TS 编译器必须对那里的声明顺序做一些奇怪的事情,我将不得不研究更多。 @DylanStewart 声明可以从.ts
文件中分解到.d.ts
文件中,但前提是它们具有不同的模块说明符。例如。 my-module.ts
和 my-module-declarations.d.ts
没问题,但 my-module.ts
和 my-module.d.ts
不没问题。 .tsx
文件也是如此。
@AluanHaddad 我对您提出的解决方案有疑问。如果我要使用声明全局方法,它可以工作,但是如果我将声明命名空间玩笑分解到一个单独的模块中,然后导入该模块,它似乎不起作用。这是一个屏幕截图。你有什么想法? d2ffutrenqvap3.cloudfront.net/items/2A1H3v0v2B3l3e1a3q06/…
@Tony 一个文件with ***import
或export
是一个模块。要影响模块内的全局声明空间,您需要使用declare global
。你的custom-matchers.ts
有一个***import
这意味着它是一个模块并且需要blockdeclare global
@Tony 您的代码所做的是声明一个模块范围的命名空间,它与jest
全局无关。【参考方案2】:
一个简单的方法是:
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);
);
【讨论】:
通过setupFilesAfterEnv配置选项,jest可以在测试前自动导入customMathers.ts
文件一次【参考方案3】:
在tsconfig.json
中添加"@types/testing-library__jest-dom"
到types
为我解决了这个问题
// tsconfig.json
"types": [
"node",
"jest",
"@types/testing-library__jest-dom"
],
也看到这个答案Property 'toBeInTheDocument' does not exist on type 'Matchers<any>'
【讨论】:
【参考方案4】:@AluanHaddad 的回答几乎是正确的,没有几种类型。 这个有效:
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,
);
有关真实示例,请参阅https://github.com/Quantum-Game/quantum-tensors/blob/master/tests/customMatchers.ts(测试实际上通过了:https://travis-ci.com/Quantum-Game/quantum-tensors)。
【讨论】:
以上是关于扩展全球公开的第三方模块的主要内容,如果未能解决你的问题,请参考以下文章