I'm trying to define a function that will take a set of functions and wrap them in MobX's flow
. My actual code does a bit more than just wrap with flow
, but that isn't important to this question. I've devised some TypeScript types that will successfully do the mapping, but am having trouble getting the function to match the types.
First I thought I could use use lodash's mapValues
like this:
const actions = {
a: function* (a: number) { return "test" },
b: function* (b: string) { return 1 }
};
const flowed = mapValues(actions, f => flow(f));
But mapValues
typing assumes that every property has the same type, so this generates an error:
(parameter) f: ((a: number) => Generator<never, string, unknown>) | ((b: string) => Generator<never, number, unknown>)
Argument of type '((a: number) => Generator<never, string, unknown>) | ((b: string) => Generator<never, number, unknown>)' is not assignable to parameter of type '(a: number) => Generator<any, string, any> | AsyncGenerator<any, string, any>'.
Type '(b: string) => Generator<never, number, unknown>' is not assignable to type '(a: number) => Generator<any, string, any> | AsyncGenerator<any, string, any>'.
Types of parameters 'b' and 'a' are incompatible.
Type 'number' is not assignable to type 'string'.ts(2345)
Doing some research and experimenting, I came up with the following generic types to do the conversion:
type Flowing<T> = T extends (...args: infer A) => Generator<any, infer R, any> ? (...args: A) => Promise<R> : never;
type Flowify<T> = {
[P in keyof T]: Flowing<T[P]>;
}
declare function flowify<T>(o: T): Flowify<T>;
const flowed = flowify(actions);
Now flowed
has the correct type:
{
a: (a: number) => Promise<string>;
b: (b: string) => Promise<number>;
}
The problem is that I haven't figured out how to create the flowify
function so that it matches the types without casting. Here is what I have so far, but I'd like to avoid having to use as
:
function flowify<T extends object>(o: T): Flowify<T> {
return mapValues(o, f => flow(f as any)) as Flowify<T>;
}
I've already worked on this task for a few hours and hope to get some expert eyes on the problem. I've got this code in a TypeScript Playground.