I have an asyncPipe function like this:
export function asyncPipe<A, B>(
ab: (a: A) => MaybePromise<B>
): (a: MaybePromise<A>) => Promise<B>;
export function asyncPipe<A, B, C>(
ab: (a: A) => MaybePromise<B>,
bc: (b: B) => MaybePromise<C>
): (a: MaybePromise<A>) => Promise<C>;
export function asyncPipe<A, B, C, D>(
ab: (a: A) => MaybePromise<B>,
bc: (b: B) => MaybePromise<C>,
cd: (c: C) => MaybePromise<D>
): (a: MaybePromise<A>) => Promise<D>;
export function asyncPipe<A, B, C, D, E>(
ab: (a: A) => MaybePromise<B>,
bc: (b: B) => MaybePromise<C>,
cd: (c: C) => MaybePromise<D>,
de: (d: D) => MaybePromise<E>
): (a: MaybePromise<A>) => Promise<E>;
export function asyncPipe(...fns: Function[]) {
return (x: any) => fns.reduce(async (y, fn) => fn(await y), x);
}
I want to call it like this:
const createRoute = (...middleware: Middleware[]) => async (
request: Response,
response: Request
) => {
try {
await asyncPipe(...middleware)({
request,
response
});
} catch (e) {
console.error(e);
response.status(500);
response.json({
error: "Internal Server Error"
});
}
};
where Middleware is a type for functions take in an object with at least the keys request and response, but might add more data the object.
{
request,
response,
user, // added by some middleware
db, // added by another middleware
/* ... more keys added by middleware */
}
TypeScript complains and says Expected 1-6 arguments, but got 0 or more. in the line in which I call asyncPipe. How Can I tell it, that middleWare will always be at least one argument, or overload it, so it accepts 0 arguments, too?
Update:
I tried Rob's answer like this:
export function asyncPipe<A>(...fns: Function[]): MaybePromise<A>;
But that prevents all of my asyncPipe specific tests to fail. The tests btw, look like this:
import { describe } from "riteway";
import { asyncPipe } from "./asyncPipe";
const asyncInc = (n: number) => Promise.resolve(n + 1);
const inc = (n: number) => n + 1;
describe("asyncPipe()", async assert => {
assert({
given: "a promise",
should: "pipe it",
actual: await asyncPipe(asyncInc)(1),
expected: 2
});
assert({
given: "two promises",
should: "pipe them",
actual: await asyncPipe(asyncInc, asyncInc)(1),
expected: 3
});
assert({
given: "three promises",
should: "pipe them",
actual: await asyncPipe(asyncInc, asyncInc, asyncInc)(1),
expected: 4
});
assert({
given: "promises mixed with synchronous function",
should: "pipe them",
actual: await asyncPipe(asyncInc, inc, asyncInc)(1),
expected: 4
});
{
const throwInc = (n: number) => Promise.reject(n + 1);
assert({
given: "promises where one throws",
should: "pipe them",
actual: await asyncPipe(asyncInc, throwInc, asyncInc)(1).catch(x => x),
expected: 3
});
}
});