title: TypeScript: conditionally convert a set of types to a different set of types based on their names
body: <p>I've written my own TypeScript ORM where the model classes used for SQL INSERTs, are separate from the model classes used when you SELECT an existing row from the database.  Reason being that all my models are immutable...</p>
  <ul>
  <li>INSERT models have optional properties on them, e.g. for fields that the database will automatically populate during INSERT using default values / SQL TRIGGERs.</li>
  <li>SELECT models don't have any optional properties, there will always be a scalar value or <code>null</code> (but never <code>undefined</code>).</li>
  </ul>
  <p>So some simple examples, let's say there's two SQL tables: <code>user</code> and <code>blog</code>, this means I have 4x separate models:</p>
  <pre class="lang-js prettyprint-override"><code>class Insert_user {
      readonly id:string;
      readonly username:string;
      readonly joined_at?:string; // Optional because: SQL will set a default value of NOW() during INSERT
  
      constructor(props:Insert_user) { Object.assign(this, props); Object.freeze(this); }
  }
  class Select_user {
      readonly id:string;
      readonly username:string;
      readonly joined_at:string; // we know it will always be there when pulling an existing record out
  
      constructor(props:Select_user) { Object.assign(this, props); Object.freeze(this); }
  
  }
  class Insert_blog {
      readonly id:string;
      readonly blog_title:string;
      readonly view_count?:number; // Optional because: SQL will set a default value of 0 during INSERT
  
      constructor(props:Insert_blog) { Object.assign(this, props); Object.freeze(this); }
  }
  class Select_blog {
      readonly id:string;
      readonly blog_title:string;
      readonly view_count:number;  // we know it will always be there when pulling an existing record out
  
      constructor(props:Select_blog) { Object.assign(this, props); Object.freeze(this); }
  
  }
  </code></pre>
  <p>I want to be able to write multiple functions that can receive the &quot;Insert&quot; models, but the typing system will know to return the corresponding &quot;Select&quot; model, based on the input.  e.g.</p>
  <pre class="lang-js prettyprint-override"><code>
  type AnyInsertModel = Insert_user | Insert_blog;
  type AnySelectModel = Select_user | Select_blog;
  
  function takeAnInsertModelButReturnItsSelectModel(insertModel:AnyInsertModel) {
      // data from insert model is INSERTed into SQL database
      // after the INSERT is done, the data is then SELECTed from the 
      const selectModel = {/* data that we pulled using SELECT */} as Select_???;
  }
  
  
  /**
   * I want the type for the output variable below to be Select_user
   */
  const selectedUser = takeAnInsertModelButReturnItsSelectModel(new Insert_user({id: 'd110ec70-9a16-4ad0-a73b-82e241a054eb', username: 'Neo'}));
  
  /**
   * I want the type for the output variable below to be Select_blog
   */
  const selectedBlog = takeAnInsertModelButReturnItsSelectModel(new Insert_blog({id: '2068bc9d-f19d-4043-a13a-6af4b2207be2', blog_title: 'I liek milk'}));
  </code></pre>
  <p>I want the types to be &quot;converted&quot; simply from function arguments, i.e. I don't want to have to give a redundant generic on every function call (generics are ok on the function definitions themselves though), seeing the argument is already typed to begin with.</p>
  <p><em><strong>Looks like I might have solved it myself (answer below), keen to hear about any other approaches too though.</strong></em></p>