1

I try to create a dead-simple function that accepts:

  1. A callback
  2. An optional array of arguments to pass to the callback

The function in plain JS would look like this:

const callCallback = (cb, args = []) => cb(...args);

The callback might accept an arbitrary number of arguments & return whatever. The problem is that I can't properly express the type of callback with TypeScript. I tried to declare callCallback's type like this (the idea was to infer callback's arguments & return types):

export interface CallCallback {
  <CbReturn, CbArgs>(
    cb: (...optionalArguments: CbArgs[]),
    args?: CbArgs[],
  ): CbReturn
};

However, this didn't work because it forces the callback to use arguments spreading syntax while it's not what I intend to do.

I also found this answer but as for now TS doesn't let me make spread argument optional.

How can I infer args number & types into cb parameters list?

1 Answer 1

3

Would the below work? It seems like you're essentially typing apply:

interface ApplyFn {
    <F extends () => any>(fn: F): ReturnType<F>;
    <F extends (...args: any[]) => any>(fn: F, args: Parameters<F>): ReturnType<F>;
}

declare const apply: ApplyFn;

apply((f: number, s: string, t: Date) => f + s + t, [23, "two", new Date]); // OK
apply((f: number, s: string, t: Date) => f + s + t, ["not a number", "two", new Date]); // ERR
apply((f: number, s: string, t: Date) => f + s + t, [123, "two"]); // ERR

apply(() => "nice!", []); // OK
apply(() => "nice!"); // OK
apply((param: number) => "nice!"); // ERR

Edit: The type above is useful if you have a hole for this type of function to fit in (e.g., a function argument). If you're actually trying to define this function, it's easier to represent it as a standard function definition:

function apply<F extends () => any>(fn: F): ReturnType<F>;
function apply<F extends (...args: any[]) => any>(fn: F, args: Parameters<F>): ReturnType<F>;
function apply(fn: Function, args: any[] = []) {
  return fn(args);
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you, so much! I don't completely get how it works because it's a way too advanced for me, but your answer certainly helped a lot.
Feel free to point out what you don’t understand and I’ll expand my answer.
That would be great, first of all, could you please add an actual function declaration in your answer? Because declare const callCallback: ApplyFn = (fn, args = []) => fn(...args); produces a type error & I can't figure out how to apply ApplyFn type.
I added the most simple way to write the actual function in TS.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.