To provide an implementation of a mock function you must know its function signature, but whichever one of the overloads you choose, TypeScript will give you an error. The fix is actually quite simple: use the signature of the actual implementation, not the overloads.
Read more: How To Mock Overloaded Functions in Jest and TypeScriptSuppose you have a function f() with two overloads and an implementation. The two overloads appear first, and have no function body. The implementation appears last. It has a function body, but importantly, its signature does not take part in overload resolution. This means that it will be missing if all you have is a types.d.ts file.
function f(x: number): void; // Overload 1.
function f(x: string): void; // Overload 2.
// Implementation.
function f(x: number | string): void {
// ...
}
// Create a variable with the type of function `f()`'s overload set.
let v = f;
v = (x: number) => { }; // Error: Does not match overload 2.
v = (x: string) => { }; // Error: Does not match overload 1.
// The solution: match the actual implementation signature.
v = (x: number | string) => { };
In the case above the solution is obvious, as the implementation’s function signature is visible. If you do not have the implementation function signature then unfortunately the TypeScript error message will not give it to you. Instead, you have to synthesize it yourself by taking the union of all the possible types of each parameter using the | operator.
A Practical Example: Mocking http.get()
import * as http from 'node:http';
// Replace the http module with a mock implementation.
jest.mock('node:http');
// Create an alias with the correct type, so that TypeScript knows we are working with a mock.
const MockHttp = http as jest.Mocked<typeof http>;
const myImplementation = function (url: string | URL, options: http.RequestOptions, callback: (res: http.IncomingMessage) => void)
{
// ...
}
// Try to mock http.get(). Error.
MockHttp.get.mockImplementation(myImplementation);
This will give you a lengthy error message telling you that your function signature is invalid, even though it matches the signature given in the documentation. At this point you notice that http.get() actually has multiple signatures. None of them will work – you have to combine them together into one.
Unfortunately the function signature of the actual implementation is not visible because it is internal to Node, however you can work it out for yourself. Look at the type definitions in @types/node/http.d.ts and combine them all together.
// From node_modules/@types/node/http.d.ts
function get(options: RequestOptions | string | URL, callback?: (res: IncomingMessage) => void): ClientRequest;
function get(url: string | URL, options: RequestOptions, callback?: (res: IncomingMessage) => void): ClientRequest;
// The implementation must accept either of these signatures, therefore:
// The first parameter is either "options" or "url", so it is either string, URL, or RequestOptions.
// The second parameter is either "callback" or "options", so it is optional and accepts either a callback or RequestOptions
// Only first overload has a third parameter, which is "callback", so it is optional and accepts a callback.
// Putting these together gives:
function (
url: string | URL | RequestOptions,
options?: RequestOptions | ((res: IncomingMessage) => void),
callback?: (res: IncomingMessage) => void
): ClientRequest;
Now you can create your implementation with that function signature and pass it through to MockHttp.get.mockImplementation().
import * as http from 'node:http';
// Replace the http module with a mock implementation.
jest.mock('node:http');
// Create an alias with the correct type, so that TypeScript knows we are working with a mock.
const MockHttp = http as jest.Mocked<typeof http>;
const myImplementation = function (url: string | URL | http.RequestOptions, options?: http.RequestOptions | ((res: http.IncomingMessage) => void), callback?: (res: http.IncomingMessage) => void)
{
// ...
}
// Success!
MockHttp.get.mockImplementation(myImplementation);
There are a few things to note when doing this:
- Combine types using
|. - Make the parameter optional with
?if it is optional in any overload. - Function signatures require an extra set of parentheses around them when used in combination with the
|operator.