title: Objects composed of generic types in TypeScript (type recursion)
body: <p>Given the following untyped TS:</p>
  <pre><code>const compose = (thunk: any): any =&gt; {
    const res = { ...thunk() };
    return { ...res, then: (f: any): any =&gt; compose(() =&gt; ({...res, ...f()})) };
  };
  
  </code></pre>
  <p>We can use it to make composable objects:</p>
  <pre><code>const { foo, bar } = compose(() =&gt; ({foo: 1})).then(() =&gt; ({bar: 2}))
  // foo: 1, bar: 2
  </code></pre>
  <p>But typing this in TS seems tricky, as the types are recursive.</p>
  <p>The best I could come up with is:</p>
  <pre><code>type Compose&lt;T&gt; = (thunk: () =&gt; T) =&gt; T &amp; { then: Compose&lt;any&gt; };
  
  const compose2 = &lt;T extends {}&gt;(thunk: () =&gt; T): ReturnType&lt;Compose&lt;T&gt;&gt; =&gt; {
    const res = { ...thunk() };
    return { ...res, then: (f) =&gt; compose2(() =&gt; ({ ...res, ...f() })) };
  };
  </code></pre>
  <p>This means that all the objects that fall out of <code>compose2</code> are of type <code>any</code>.
  The end result I'd like to accomplish is something that's typed with all of the composed objects:</p>
  <pre><code>const combined = compose(() =&gt; ({foo: 1})).then(() =&gt; ({bar: 2}))
  // type of combined: { foo: number } &amp; { bar: number } &amp; { then: … }
  </code></pre>
  <p>The way I see it we'd need some sort of <code>Fix</code> type that can tie the recursive knot, as the type for <code>then</code> in <code>Compose</code> needs to recurse.</p>
  <p>Of course, there might be a way to do it if we inverted the signature of <code>compose</code> and somehow used CPS. I'm open for suggestions!</p>
  <p>Note that a 2-ary <code>compose</code>, let's call it <code>combine</code>, is no issue:</p>
  <pre><code>const combine = &lt;A extends {}, B extends {}&gt;(a: () =&gt; A, b: () =&gt; B): A &amp; B =&gt; ({...a(), ...b()});
  const bar = combine(() =&gt; ({foo: 1}), () =&gt; combine(() =&gt; ({bar: 2}), () =&gt; ({baz: 3})) )
  </code></pre>
  <p>But it isn't particularly nice to write the statements, so I hoped to pass a closure from the resulting object so I wouldn't have to nest repeated function calls.</p>