Harshal Patil
Harshal Patil

Reputation: 21000

How to make overloaded higher-order function work correctly with optional arugments in TypeScript?

I have a function that returns a promise which gets some data using fetch:

export function funcWithOptsArgs(a: string, b: number, c?: string): Promise<number> {
  // Some API call.
  return fetch('/blah/blah').then(() => Promise.resolve(10));
}

I have written a custom hook to manage API calls from my components. It is defined as an overloaded function to accept a callback with variable number of arguments.

export type StreamHook<Data, Callback> = [{ loading: boolean, data: Data | null, error: any }, Callback];

// Custom react hook for API call
export function useAsyncAction<Data>(callback: () => Promise<Data>): StreamHook<Data, () => void>;
export function useAsyncAction<A1, Data>(callback: (a1: A1) => Promise<Data>): StreamHook<Data, (a1: A1) => void>;
export function useAsyncAction<A1, A2, Data>(callback: (a1: A1, a2: A2) => Promise<Data>): StreamHook<Data, (a1: A1, a2: A2) => void>;
export function useAsyncAction<A1, A2, A3, Data>(callback: (a1: A1, a2: A2, a3: A3) => Promise<Data>): StreamHook<Data, (a1: A1, a2: A2, a3: A3) => void>;
export function useAsyncAction<Data>(callback: (...args: any[]) => Promise<Data>): StreamHook<Data, any> {
  // TODO: Implementation
  return {} as any;
}

When I use it in my component, it works perfectly well except for the optional argument - c in the above example.

// Usage
function MyComp() {

  const [data, doFetch] = useAsyncAction(funcWithOptsArgs);

  // Somewhere within the component
  // Works perfectly well.
  doFetch('Harshal', 32);

  // ERROR: Typescript error. Not accepting third argument
  doFetch('Harshal', 32, 'Patil');

}

Basically, it interprets doFetch as doFetch: (a1: string, a2: number) => void while ignoring the optional argument. I tried many different styles to write the signature but nothing seems to work.

Any solution to make this work?

Upvotes: 2

Views: 45

Answers (1)

aleksxor
aleksxor

Reputation: 8340

You can try to use tuple type in rest parameters:

export function useAsyncAction<Args extends readonly unknown[], Data>(callback: (...args: Args) => Promise<Data>): StreamHook<Data, (...args: Args) => void> {
  // TODO: Implementation
  return {} as any;
}

playground link

Upvotes: 1

Related Questions