What I'm trying to do is create a series of nested functions which can be chained together, and the final callback's arguments will be a union of the parents. Example of my failed attempt so far:
export type SomeHandler<T> = (args: T) => void;
type WithFooArgs = { foo: string }
const withFoo = <T,>(handler: SomeHandler<T & WithFooArgs>) => (args: T & WithFooArgs) => handler(args);
type WithBarArgs = { bar: string }
const withBar = <T,>(handler: SomeHandler<T & WithBarArgs>) => (args: T & WithBarArgs) => handler(args);
type WithZooArgs = { zoo: string }
const withZoo = <T,>(handler: SomeHandler<T & WithZooArgs>) => (args: T & WithZooArgs) => handler(args);
export default withFoo(withBar(withZoo((args) => {
// I want args to be type `WithFooArgs & WithBarArgs & WithZooArgs`
})));
The goal is to have a bunch of these which I can chain together in different ways.
CodePudding user response:
You are trying to change the generics of withZoo(..)
based upon the expressions around it which is not possible. You could create a single generic that takes in these middleware-like callbacks then use the data from those callbacks to type a single callback function like the following:
export type SomeHandler<T> = (args: T) => void;
type WithFooArgs = { foo: string }
const withFoo = <T,>(handler: SomeHandler<T & WithFooArgs>) => (args: T & WithFooArgs) => handler(args);
type WithBarArgs = { bar: string }
const withBar = <T,>(handler: SomeHandler<T & WithBarArgs>) => (args: T & WithBarArgs) => handler(args);
type WithZooArgs = { zoo: string }
const withZoo = <T,>(handler: SomeHandler<T & WithZooArgs>) => (args: T & WithZooArgs) => handler(args);
// https://stackoverflow.com/a/50375286/10873797
type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never;
function createHandler<Handler extends SomeHandler<any>>(handlers: Handler[], cb: (args: UnionToIntersection<Parameters<Parameters<Handler>[0]>[0]>) => void) {
return (args: any) => {
return cb(handlers.reduce((acc, cur) => cur(acc), args));
};
}
export default createHandler([withFoo, withBar, withZoo], (args) => {
console.log(args); // WithFooArgs & WithBarArgs & WithZooArgs
});