#How to create a type-safe function that accepts other functions in array form?

7 messages · Page 1 of 1 (latest)

median geode
#

I want to use a function like this:

  await checkTasks(
    [foo, 123],
    [bar, "lol"],
  );

Here, foo and bar are functions:

async function foo(arg: number) {}
async function bar(arg: string) {}

I want the checkTasks function to be type safe such that if I try to write something like this:

  await checkTasks(
    [foo, 123, "someBogusExtraArgument"],
    [bar, "lol"],
  );

It would give a type error.

My naive attempt at writing this function is as follows:

type Task<T extends (...args: any[]) => any> = [T, ...Parameters<T>];

export async function checkTasks<
  T extends [...Array<Task<(...args: any[]) => any>>],
>(...tasks: T): Promise<void> {
  for (const [fn, ...args] of tasks) {
    await fn(...(args as any));
  }
}

However, it does not seem to work properly. Meaning that I do not get a type error on "someBogusExtraArgument".

For reference, here is the code in the TypeScript playground (see link below).

How do I write this function correctly?

silent flickerBOT
#
zamiel#0

Preview:```ts
async function main() {
await checkTasks([foo, 123], [bar, "lol"])

await checkTasks(
[foo, 123, "someBogusExtraArgument"],
[bar, "lol"]
)
}

async function foo(arg: number) {}
async function bar(arg: string) {}

type Task<T extends (...args: any[]) => any> = [
T,
...Parameters<T>
]
...```

#
lukeabby#0

Preview:```ts
async function main() {
await checkTasks(
[foo, 123],
[bar, "lol"],
);

await checkTasks(
  [foo, 123, "someBogusExtraArgument"],
[bar, "lol"],
  );
}

async function foo(arg: number) {}
async function bar(arg: string) {}

// This usage of any is not typesafe. If you want it to be typesafe write ...args: never but it will break checkTasks.
...```

tawny eagle
#

This should do what you want.

median geode
#

Amazing, thank you @tawny eagle !
One other question:

What happens if I pass a non-async function to checkTasks?
Will everything still work properly?

tawny eagle
#

should work fine!

#

await does nothing if it's not a thenable