4

How can I define a function in typescript that takes a function as the first parameter, and the arguments for that function as the second parameter, while maintaining the strong typing of the function passed as the first parameter?

For example, say I have three functions defined as follows:

const firstFunc = (param1: string, param2: number): string;
const secondFunc = (param1: boolean): void;
const thirdFunc = (): number;

I'd like to create a function like this (not working code):

const myFunc = (someFunc: () => any, params: []) => {
  return someFunc(...params);
}

Then if I were to pass firstFunc into myFunc, it should require params to be a string and a number, and expect to return a string. If I passed secondFunc into myFunc, it should require params to be a boolean, and expect to return void, and if I pass thirdFunc, it should be fine with nothing passed to params, and expect to return a number.

Is something like this possible?

1 Answer 1

7

This should do it:

const callMeMaybe = <T extends (...args: any[]) => any>(f: T, ...args: Parameters<T>): ReturnType<T> => {
  return f(...args);
}

const heyIJustMetYou = (a: number, b: string): string => b.repeat(a)

// errors as expected
const andThisIsCrazy = callMeMaybe(heyIJustMetYou, 3);

// Has type string
const butHeresMyNumberSo = callMeMaybe(heyIJustMetYou, 3, 'a');

Playground

Note that this doesn't work if f is itself generic. Also note that this isn't terribly useful. But you could make it return a thunk, and that would probably be more useful:

const thunkify = <T extends (...args: any[]) => any>(f: T, ...args: Parameters<T>): () => ReturnType<T> => {
  return () => f(...args);
}

...which returns a function that when called will call f with its arguments. This can be used to delay evaluation of f.

UPDATE

Better version, courtesy of jcalz in the comments:

const callMeMaybe = <A extends any[], R>(f: (...args: A) => R, ...args: A): R => {
  return f(...args);
}

Properly handles generic functions!

Sign up to request clarification or add additional context in comments.

5 Comments

Actually it seems to work ok with f being generic
@Aadmaa it will "work" in the sense that you won't get a compilation error necessarily, but if f returns a generic type it won't be fully realized.
This worked, thanks! I'm actually using this as a react hook to make asynchronous calls to the backend, and just figured adding all of that info would muddy the question :)
Note that the preferred way to do this is with separate type parameters for the rest argument and for the return type, as shown here, which also handles generic functions (since TS3.4 introduced such support). Not sure if you want to update your answer to include this or if you think I should write my own answer post.
@jcalz thanks, I'll edit, although you are always welcome to write your own answer as well

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.