Home > Net >  Typescript complaining after passing ...args as argument to a function
Typescript complaining after passing ...args as argument to a function

Time:08-15

I'm using fetch-mock library to build my own utils.

When defining a new function and passing args to fetchMock.mock function, I get this error:

A spread argument must either have a tuple type or be passed to a rest parameter

export function mockRequest(...args: any) {
  return fetchMock.mock(...args);
}

I could strictly define the amount of arguments, but would want to avoid it. Any suggestions how to keep ...args and make TypeScript happy?

I've tried various solutions such as using .apply and doing some casts as well but to no avail.

CodePudding user response:

Problem

fetchMock.mock is typed to accept 0-3 arguments:

mock(matcher: MockMatcher | MockOptions, response: MockResponse | MockResponseFunction, options?: MockOptions): this;
mock(options: MockOptions): this;
mock(): this;

wheresrhys/fetch-mock/types/index.d.ts:L270-L294

An attempt is made to spread an array of any into it. any will not stop TypeScript from checking the potential length of the spread – it will only not check the type of the items in the tuple are acceptable for the function parameters. An example:

function fn(a: number, b: number) {}

const foo: any[] = [];
const bar: [any, any] = ["one", "two"];

fn(...foo); // Not OK ❌
// ~~~~~~
// A spread argument must either have a tuple type or be passed to a rest parameter.

fn(...bar); // OK, even though we're passing in strings ✅

TypeScript Playground

Solution

The error message tells you the solution (emphasis mine):

A spread argument must either have a tuple type or be passed to a rest parameter.

Let's explore both of them.

Passing to a rest parameter

If the function is typed to accept a rest parameter it works because rest parameters are essentially an infinite list of optional parameters. However, in your case, you don't have control of the interface of the library. Consequently, you'll have to use the other option.

Type the argument as a tuple

Tuples are a list that have two notable properties. They are:

  • Finite. This guarantees the length will match the length of the arguments to the function you're attempting to use.
  • Ordered. This guarantees the type of a value at each index will match with the function's argument at the same index.

You could manually create a tuple type that is acceptable for fetchMock.mock's interface. However it's manual. Handily, TypeScript provides a utility function named Parameters which constructs a tuple type from the types used in the parameters of the function passed to it:

function mockRequest(...args: Parameters<typeof fetchMock.mock>) {
  return fetchMock.mock(...args);
}

TypeScript Playground

CodePudding user response:

Try making args an array type.

export function mockRequest(...args: any[]) {
    return fetchMock.mock(...args);
}
  • Related