title: In TypeScript, defining a function that maps a set of functions
body: <p>I'm trying to define a function that will take a set of functions and wrap them in MobX's <code>flow</code>. My actual code does a bit more than just wrap with <code>flow</code>, 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.</p>
  
  <p>First I thought I could use use lodash's <code>mapValues</code> like this:</p>
  
  <pre><code>const actions = {
      a: function* (a: number) { return "test" },
      b: function* (b: string) { return 1 }
  };
  
  const flowed = mapValues(actions, f =&gt; flow(f));
  </code></pre>
  
  <p>But <code>mapValues</code> typing assumes that every property has the same type, so this generates an error:</p>
  
  <pre><code>(parameter) f: ((a: number) =&gt; Generator&lt;never, string, unknown&gt;) | ((b: string) =&gt; Generator&lt;never, number, unknown&gt;)
  Argument of type '((a: number) =&gt; Generator&lt;never, string, unknown&gt;) | ((b: string) =&gt; Generator&lt;never, number, unknown&gt;)' is not assignable to parameter of type '(a: number) =&gt; Generator&lt;any, string, any&gt; | AsyncGenerator&lt;any, string, any&gt;'.
    Type '(b: string) =&gt; Generator&lt;never, number, unknown&gt;' is not assignable to type '(a: number) =&gt; Generator&lt;any, string, any&gt; | AsyncGenerator&lt;any, string, any&gt;'.
      Types of parameters 'b' and 'a' are incompatible.
        Type 'number' is not assignable to type 'string'.ts(2345)
  </code></pre>
  
  <p>Doing some research and experimenting, I came up with the following generic types to do the conversion:</p>
  
  <pre><code>type Flowing&lt;T&gt; = T extends (...args: infer A) =&gt; Generator&lt;any, infer R, any&gt; ? (...args: A) =&gt; Promise&lt;R&gt; : never;
  
  type Flowify&lt;T&gt; = {
      [P in keyof T]: Flowing&lt;T[P]&gt;;
  }
  
  declare function flowify&lt;T&gt;(o: T): Flowify&lt;T&gt;;
  
  const flowed = flowify(actions);
  </code></pre>
  
  <p>Now <code>flowed</code> has the correct type:</p>
  
  <pre><code>{
      a: (a: number) =&gt; Promise&lt;string&gt;;
      b: (b: string) =&gt; Promise&lt;number&gt;;
  }
  </code></pre>
  
  <p>The problem is that I haven't figured out how to create the <code>flowify</code> 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 <code>as</code>:</p>
  
  <pre><code>function flowify&lt;T extends object&gt;(o: T): Flowify&lt;T&gt; {
      return mapValues(o, f =&gt; flow(f as any)) as Flowify&lt;T&gt;;
  }
  </code></pre>
  
  <p>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 <a href="https://www.typescriptlang.org/play/#code/C4TwDgpgBA8gRgKwgY2ASWBATgQ2AeywB4AVeJVAGihICUIBnAVwBtgA+KAXigAoA3HCyYQAXDXIpgAbQDWEEPgBmExFIC61eSHENgWAJYA7AObVk+FiykH8R8WTWoAlN051GrYAG4AsAChQSCgAWRwwADUhEQZuKFIoCAAPTCMAE1j8J2BqD2Y2dl4shAdzIRY4HGRZcUlUDGw8QlJc+nyOVy5OAG8oaQAFKGMobWUadQc2rygAXz9-AIsjPSgAW3Co4UZxMMjoxjiixGoDTFxMCE6egKhbqCWVrE82OO6ZqBxYnCMQeZu7urAAB02gYRwQziBSkIAFEqgALXi8bRXKDdf53O5PdpyBTqOKnRoXcG4kAfWKjFRBCBjYqaEYKajFZzzTEzFkBDFQJ7AJhYIzc54+AIzTmBcDQABiLHwAHc4kRaNQAIJYEyxZKpDIfH7SdSFEwQIxEwjiXhAi04NUMcSq9WogDiRpNxG+IGoSp1IHYqPNluttutqP6WHwqwMDAgivY8zSKBYVugD2AUCUMtl4mlcr+-mTH1QtmWry5OHESiYRgLdgAVHxS1AjExVnBsK5ejy+QKAESYPRd2aULlwMsVqtGWu8YdQPSGUxtwW8-lQACMsxFObzablEDSq546z2WzBVWAhYY1BUXVT6d4SmcHIW4uCWdlxhMpE4PBIiRSRu1fqBK11XEYwlGwKBlUdZ1zmaN0TiMMCsCgT03U4AB+PgLUAgMIODUNw0jaMoHEY1+GwHNqSgF8DCUEAP2LfxMQGIYBUpcZM3TN9SAGfV5lFR9y0rU87GvOUaLo79NT-TJskKfAHGcDixNo+j0UYrEIEXAUD02GIigvNxRNlW9yS9e9TOolSSBjEUxU3dMdwAJjiLdX1o3gTzPB8AkoiwsCeVASAlOJqTGVynPmIA" rel="nofollow noreferrer">TypeScript Playground</a>.</p>