My project is strictly typed, with many limitations. Let's say I have some class with two methods.
class SomeClass {
public someMethod(param1: string, param2: number): string {
return `${param1}: ${param2}`;
}
public otherMethod(param3: string[]): boolean {
return !!param3.length;
}
}
I want to mock both functions, what normally I do: const mock = jest.fn() unfortuantely, strict types + eslint rules complain in many places for such approach. Few examples:
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)
I can provide types explictily:
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
It is working but quite annoying (especially if we change function signatures, etc)
To avoid that I can make:
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
This looks nicer, but I need to repeat this ReturnType<SomeClass['someMethod']>, Parameters<SomeClass['someMethod']> a lot, would be good to have some automatic function for that.
That is where I fail, I tried for example reproduce it with following:
const typedJestFn = <T, K extends keyof T>() => jest.fn<ReturnType<T[K]>, Parameters<T[K]>>();
const mock4 = typedJestFn<SomeClass, 'someMethod'>();
But it gets:
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'.
Strangely enough the mock4 itself works as expected
PS:
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"alwaysStrict": true,