jest.fn的自动类型用于模拟的类方法

问题描述

我的项目是严格输入的,有很多限制。假设我有一些带有两种方法的类。


Public Sub DDtest()

Dim EDay As Date
Dim ETime As Date
Dim DtgA As Date

EDay = Format(CDate(Replace(Worksheets("Data2020").Range("E2").Value,".","/")),"dd-mmm-yyyy")
ETime = Format(Worksheets("Data2020").Range("F2"),"hh:mm:ss")
DtgA = EDay + ETime

Dim EDay2 As Date
Dim ETime2 As Date
Dim DtgB As Date

EDay2 = Format(CDate(Replace(Worksheets("Data2020").Range("E3").Value,"dd-mmm-yyyy")
ETime2 = Format(Worksheets("Data2020").Range("F3"),"hh:mm:ss")
DtgB = EDay2 + ETime2

Dim result As Date
result = Format(DateDiff("s",DtgA,DtgB) / (60 * 60),"hh:mm:ss")

MsgBox "Date 1:" & DtgA & vbNewLine & "Date 2:" & DtgB & vbNewLine & vbNewLine & DateDiff("s",DtgB) / (60 * 60) & vbNewLine & result


End Sub

我想模拟这两个函数,通常我会这样做:class SomeClass { public someMethod(param1: string,param2: number): string { return `${param1}: ${param2}`; } public otherMethod(param3: string[]): boolean { return !!param3.length; } } 不幸的是,严格的类型+护送规则在很多地方抱怨这种方法。几个例子:

const mock = jest.fn()

我可以明确提供类型:

const mock = jest.fn();
mock.mockReturnValue(34); // no return value error
mock.mock.calls[0][0]; // ESLint: Unsafe member access [0] on an any value.(@typescript-eslint/no-unsafe-member-access)

这是有效的,但很烦人(特别是如果我们更改功能签名等)

为避免这种情况,我可以这样做:

const mock = jest.fn<string,[string,number]>();
mock.mockReturnValue(34); // TS2345: Argument of type '34' is not assignable to parameter of type 'string'
mock.mock.calls[0][0]; // ok

这看起来更好,但是我需要重复很多const mock1 = jest.fn<ReturnType<SomeClass['someMethod']>,Parameters<SomeClass['someMethod']>>(); const mock2 = jest.fn<ReturnType<SomeClass['otherMethod']>,Parameters<SomeClass['otherMethod']>>(); mock1.mockReturnValue(34); // error,TS2345: Argument of type '34' is not assignable to parameter of type 'string' mock1.mockReturnValue('34'); // ok mock2.mock.calls[0][1]; // error,TS2493: Tuple type '[string[]]' of length '1' has no element at index '1'. mock2.mock.calls[0][0]; // ok ,为此可以有一些自动功能

那是我失败的地方,例如,我尝试通过以下方式重现它:

ReturnType<SomeClass['someMethod']>,Parameters<SomeClass['someMethod']>

但是它得到了:

const typedJestFn = <T,K extends keyof T>() => jest.fn<ReturnType<T[K]>,Parameters<T[K]>>();
const mock4 = typedJestFn<SomeClass,'someMethod'>();

奇怪的是,mock4本身可以正常工作

PS:

TS2344: Type 'T[K]' does not satisfy the constraint '(...args: any) => any'.
   Type 'T[keyof T]' is not assignable to type '(...args: any) => any'.
     Type 'T[string] | T[number] | T[symbol]' is not assignable to type '(...args: any) => any'.
       Type 'T[string]' is not assignable to type '(...args: any) => any'.

解决方法

该问题的问题在于,首先我们创建一个通用类型T,然后从该K中获取一个密钥T。使用T[K],我们尝试访问该成员,该成员可以是任何类型(不受模板类型限制)。

因此,尽管很难从类中过滤函数(方法),但事实证明我们根本不需要它。我们只需要任何函数,T可以只是一个函数。

这是我最终得到的解决方案:

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const typedJestFn = <T extends (...args: any) => any>() => jest.fn<ReturnType<T>,Parameters<T>>();

效果不佳,有any,但是它没有任何影响,只是通知T应该是函数类型。