For my current project, I am designing a custom route handler creator for Express. The goal is to allow passing arbitrary assertions as initial arguments before invoking the route handler callback. Here's an example of how I envision it:
const myHandler = makeHandler(assertion1(), assertion2(), (data, req, res, next) => {
// data contains results from the assertions
});
I have managed to achieve some progress in defining the types as needed:
// declaration of Assertion and ReturnTypes
type Assertion<T = unknown> = (req: express.Request) => T;
type ReturnTypes<A extends ReadonlyArray<Assertion>> = {
[K in keyof A]: ReturnType<A[K]>;
};
function assertion1<T extends object>(arg: T) {
return () => arg
}
function assertion2() {
return () => "yes"
}
const a = assertion1({ something: "yes" })
const b = assertion2()
// The expected type here is [{ something: string }, string]
type d = ReturnTypes<[typeof a, typeof b]>
However, when attempting to extend this concept to the arguments of makeHandler
, there seems to be an issue where the type of data
ends up being unknown[]
:
// logic for `makeHandler`
declare function makeHandler<
Assertions extends Assertion<unknown>[]
>(...assertionsAndCb: [...Assertions, HandlerCb<ReturnTypes<Assertions>>]): void
// 'data' isn't correctly typed here. It should match the same type as `d` above.
makeHandler(assertion1({ hey: "what"}), assertion2(), (data, req) => {
return { response: {} }
})
I've tried researching similar concepts like zip
to improve my function, but I'm struggling to get the types to pass accurately. Is there a missing element or incorrect generic that is preventing proper inference?