title: "Switch" function over discriminated union members in Typescript
body: <p>Suppose I have a discriminated union in Typescript:</p>
  <pre class="lang-js prettyprint-override"><code>type MyAction = { type: &quot;redirect&quot;, url: string} | {type: &quot;returnValue&quot;, value: number}
  </code></pre>
  <p>I want to write a &quot;switch expression&quot; which takes a member of the discriminated union and a mapping of types to processor functions, and returns the result of running the correct processor function:</p>
  <pre class="lang-js prettyprint-override"><code>let actionInstance = {type: &quot;returnValue&quot;, value: 123} as MyAction
  console.log(switchExpression(actionInstance, {
    redirect: (action) =&gt; action.url + &quot;(url)&quot;,
    returnValue: (action) =&gt; `${action.value}` + &quot;(value)&quot;}
  ))
  </code></pre>
  <p>I want the processors to be typed - that is, I want Typescript to error if not all types are represented in the switch, if I provide a key for the switch which is not a valid type, or if I assume the wrong type in a processor function (like I try to access <code>action.value</code> in the value for <code>redirect</code>).</p>
  <p>This is what I've got so far (<a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAsiCCBjYBLA9gOygXigbylEgC4oAiAJwgBMUrkyAaKAVwoBtSBnYClDAOYBfKAB98RCKUoRgbDADUAhuxYQmUAG4q1pDCwC2AIwgUhAWABQVyVABySihTQB3JKkwAeACrM7APhwobygIAA9gCAxqLnxCcCl7KBEAfmCoPQhNUwBuKxsEqABlOWQ2CAAlCEQ0CmpPADEWDGR0DCq5CgxvBMDsKyhBuIBtAGkoflgEVsxhskkyAF1F0gAKbVVEhydXdzbPOD3MZlH-AEocQKaWj3bZeR7IC2tLADNmmawuFxRgRAALACiYTAVC4XH210+HQevXWOkSh0+zEQSi4EC4pBKLDKVCqNTqjQ+txhXUeEHOpChJPuZMKeAGUHYsigryCqPRXGGGzUADpJIsoGioPDNqQlBgQBdsFdiW1Sd0EnlLIMqJ0sK9RWozlZnlZmcAhZ8AJIYHgSxDQXB4STSNXyZSbDQ8xIARgATABmETCpG3Kw1M1oZm89hoASrb6-AHA0EYiGYVZKE1m4AWiDMBkqqBUWj0YBrZO3aWBIttXlsdhQADU5FWlbOTEZ9q6jt0IrLmBLUAABgASPCdjC8l1CHs1usuxvPM46yxAA" rel="nofollow noreferrer">playground link</a>), based on a few other answers on StackOverflow:</p>
  <pre class="lang-js prettyprint-override"><code>type NarrowAction&lt;T, N&gt; = T extends { type: N } ? T : never;
  
  type StuctureRecord&lt;FunctionReturnType&gt; =
      { [K in MyAction[&quot;type&quot;]]: (value: NarrowAction&lt;MyAction, K&gt;) =&gt; FunctionReturnType }
  
  function switchExpression&lt;FunctionReturnType&gt;(value: MyAction, cases: StuctureRecord&lt;FunctionReturnType&gt;): FunctionReturnType {
    let f = cases[value.type] as (value: any) =&gt; FunctionReturnType
    return f(value)
  }
  </code></pre>
  <p>This works correctly, but it's specific to one discriminated union (<code>MyAction</code>).</p>
  <p>Is there a way to adapt this code to work with <em>any</em> discriminated union where all the members have a <code>type</code> field, instead of having it only work for <code>MyAction</code>? I tried using a type like <code>DUType extends { type: string }</code>, but that results in a bunch more errors (<a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAsiCCBjYBLA9gOygXigbylEgC4oAiAJwgBMUrkyAaKAVwoBtSBnYClDAOYBfKAB98RCKUoRgbDADUAhuxYQmUAG4q1pDCwC2AIwgUhAWABQVyVABySihTQB3JKkwAeACrM7APhwobygIAA9gCAxqLnxCcCl7KBEAfmCoPQhNUwBuKxsEqABlOWQ2CAAlCEQ0CmpPABEAVW9C8Mjo2LxJbl5%20YWYAMRYMZHQMKrkKDFbIQOwrKCW4gG0AaSh%20KGbZiBWySTIAXSPSAAptVUSHJ1d3cc84e8xmNf8AShxA4dGPCdl5LsLNZLAAzEZjTBQLguFDARAACwAomEwFQuFwHjs2hEojEJAlenxBEIhhC-pNAQl-BcdIknpCMMxEEouBAuKQSiwylQqjU6o0Wgkyb9xpTprsPqQfozxTNCnhFlB2LIoKCgiy2VwVpc1AA6SRHJUAemNUAAAsAuABaFACDC1CBKqhTLCg2lXd5WYFWFXAKBKRkASQwPCUo2guG6hPILvkyiuGl1iQAjAAmADMIlZsAQjKsNVDaBVevYaAEZxhcMRKLR7MxmDOgb%20IbDEeYissSyotHowHOzfGn2wgUHmD1bHYUAA1OQzpP3kxnQDpgndFAm4zh4EAAYAEjwY4weuTQh3M7nycXwPeXssQA" rel="nofollow noreferrer">playground link</a>)</p>
  <p>Note: I know I can use the <code>StructuredRecord</code> directly or I could just use a regular <code>switch</code> statement or if/else statements with an <code>assertUnreachable</code> function (<a href="https://stackoverflow.com/questions/39419170/how-do-i-check-that-a-switch-block-is-exhaustive-in-typescript">StackOverflow post</a>), but I'm interested in whether a solution in the above form exist due to academic curiosity, and that fact that it would be an ergonomic util function to have to ensure type safety without having to type all the type annotations directly.</p>