title: How to define a dynamic generic constraint in typescript
body: <p>I have a abstract base class with a generic type parameter like this:</p>
  <pre class="lang-js prettyprint-override"><code>abstract class Base&lt;T&gt; {
    constructor(protected value: T) {}
  }
  </code></pre>
  <p>I inherit multiple classes from the base. For example:</p>
  <pre class="lang-js prettyprint-override"><code>class TBase extends Base&lt;number&gt; {
    constructor(value: number) { 
      super(value); 
    }
  }
  
  class XBase extends Base&lt;string&gt; {
    constructor(value: string) { 
      super(value); 
    }
  }
  </code></pre>
  <p>Now I want to write a factory function that returns a new Base based on my input properties:</p>
  <pre class="lang-js prettyprint-override"><code>type ValidTypes = 'string' | 'integer' | 'decimal' | 'boolean' | 'datetime';
  
  type validFor&lt;T&gt; = T extends 'string' | 'datetime'
    ? string
    : T extends 'integer' | 'decimal'
    ? number
    : T extends 'boolean'
    ? boolean
    : never;
  
  function getBase&lt;T extends ValidTypes, P extends validFor&lt;T&gt;&gt;(type: T, value: P): Base&lt;P&gt; {
   switch(type) {
     case 'number': new TBase(coerceNumber(value)); break;
     ...
   } 
  }
  </code></pre>
  <p>When passing 'string' as first parameter, the second can only be of type string. For 'decimal', type P can only be a number.</p>
  <p>But I have two problems. When calling the function like this:</p>
  <pre class="lang-js prettyprint-override"><code>getBase('string', '5');
  </code></pre>
  <p>It works, but it says that the signature is</p>
  <pre><code>function getBase&lt;'string', '5'&gt;(type: 'string', value: '5'): Base&lt;'5'&gt;
  </code></pre>
  <p>It don't understand why it's not resolving to string but instead to the value of value?</p>
  <p>The other problem is, that when I return a new TBase() it states that it could also be a string or boolean:</p>
  <p>&quot;'number' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'string | number | boolean'.&quot;</p>
  <p>I searched a lot about this, but couldn't get around it. Could someone explain to me why excactly this happens? Even when i explicit cast it to a Base it throws the error (return new TBase() as Base)</p>
  <p>Another approach I tried was with using function overloads, but this looks kinda weird and not right:</p>
  <pre class="lang-js prettyprint-override"><code>getBase(type: 'decimal' | 'integer', value: number): Base&lt;number&gt;
  getBase(type: 'string' | 'datetime', value: string): Base&lt;string&gt;
  getBase(type: 'boolean', value: boolean): Base&lt;boolean&gt;
  getBase(type: ValidTypes, value: number | boolean | string): Base&lt;number | boolean | string&gt; {
    ...
  }
  </code></pre>
  <p>I want to something like this:</p>
  <pre class="lang-js prettyprint-override"><code>
  getBase('string', 'thisMustBeAString'); // =&gt; Base&lt;string&gt;;
  getBase('decimal', 54 /* Must be a number */) // =&gt; Base&lt;number&gt;;
  
  </code></pre>
  <p>What am I missing? I'm quiet struggling with this for a long time now.. Thanks in advance</p>
  <p>Edit:</p>
  <p><a href="https://typescript-play.js.org/#code/FDCGCMGcBcCdQMbQAQIDakpZAhTBTAHgBUA+ZAb2GVQHsA7GWAVyVtgAoAHWW6fJPgAmyAG6g0zfAC5kxAJSUAvsBXB0mbMTyR8yfAA9+9Idh1F6zALbh8sclRoIGTVtHYdxkmcks27ihTI1DTIkMxcdp4SUvIA3ME0KmoaWMgAGub6RvgmZgSETACW9ADmDiHOjHBuHl5SssVlgYmh4ZGc9fjxrckg0ACekcgAahJFQsRD+NgAvMgA5E2lC8gAPosl-KV2qxsLQgJFVhJ7i+C0tGj4oPRnB6D80Mf4C3H902LjQgBi7CTkebEbLGUyLZb3ISPfDPKyvEIAfjCcBKpRCsmBhlB2AWW3wO1gkKOJzQC0Rvmstlg6LkINyYIWFyuNzu5KZ11uNPo+FEdnewAAZsx6Egigw6HYEPgAHKUqJdWTCgDW9FoAHd6PJZH4qZQQjQigLkBwAIRFSDS0DS7igWC6H5oWiPaLeZCYZGwVHyRQAMh9yDNFqtHFl-k6MW63r1oRjyFgMOYsHoyFDVJdsXeMaU+jQumjsZo8egieTAAZM6FkvyAPTV5AAQS4PCdCAAFsgGPgQEKRc9xTtoOYSHS8qNvlNIpAADTIAAKI7BXgmf1gANIHEGkQxM4Vc61uAKs4qYTVRWgbY300C+oQBEWOt2siLJd8+DVcnMHGckplcvD3m9BJazkVtzWQAVQCKXNkFsW9mDzM86GYNARAkSBaBgvRQA9VEO1gGDLg5egEX1Q4IOQ6BZGgVteHfbl3wAUVgXhOHiahs1UEAQGAxtm0QdtoDVWhu2FUV+xhHBP03HwDmJU51k2ehtl2HcI21P99yHB97EFUS+2TAdJIIS8t3BFEykhaFYVeVTvEaczSk0gpllIXTezFAyJKk6ZZEZQiWQWWyGgI5lbic3RCHZFlXJ7MTPMHbzTLGNAJgnGYgp8bSFKi24FOWcKLD-bL-NyjYXPzGhIFPc9WxM7oKtjW881xJT8UfOMEyTV9320Yzv1gKVU3lCNAP1WMyNACiqJo9VuuQJiWI4Nis2CNRgOIGZoEgEBDOMpYHMCxZsIhHpgNmchYv05BdoigAiZZbpnW7joc2712k2R7tejLPpez0ylugrCGenCAdc4BDM-fb-pWGcAFZTrrAARWgZl8PhkCE2AlUwuC8y6ZArHglBbFBtFIb2qEnheQ6Fj+1EFkR5AAHV2CVbBYMmvMqZhF5X2EbB6bKIA" rel="nofollow noreferrer">Playground Link</a></p>