title: TypeScript: Using Mapped Types with Generics
body: <p>I've been experimenting with TypeScript with the hope of making a type-safe database query library (loosely based on Scala's Slick). I've made some good progress with the help of Mapped Types but I'm getting stuck on preserving the underlying type of the Column. See the code below for a comprehensive example:</p>
  
  <pre><code>class Table {}
  
  class TableExpression&lt;TK extends Table&gt; {
    table: TK
    alias: string
    constructor(table: TK, alias?: string) {
      this.table = table
      this.alias = alias || table.constructor.name
    }
  }
  
  class Column&lt;T&gt; {
    name: string
    defaultValue?: T
  }
  
  class StringC extends Column&lt;string&gt; {
    constructor(name: string, defaultValue?: string) {
      super()
      this.name = name
      this.defaultValue = defaultValue
    }
  }
  
  class ColumnExpression&lt;TK extends Table, CK extends Column&lt;any&gt;&gt; {
    table: TableExpression&lt;TK&gt;
    column: CK
    alias: string
    constructor(table: TableExpression&lt;TK&gt;, column: CK, alias?: string) {
      this.table = table
      this.column = column
      this.alias = alias || column.name
    }
  
    eq(val: any): string {
      return `${this.table.alias}.${this.column.name} = "${val}"` // Obviously, not safe.
    }
  }
  
  class E1 extends Table {
    name = new StringC('name')
    slug = new StringC('slug')
  }
  
  let e1 = new E1()
  let ee1 = new TableExpression(e1, 'e1')
  
  type TableQuery&lt;TK extends Table&gt; = {
    [P in keyof TK]: ColumnExpression&lt;TK, TK[P]&gt;
  }
  
  function query&lt;TK extends Table&gt;(te: TableExpression&lt;TK&gt;): TableQuery&lt;TK&gt; {
    let result = {} as TableQuery&lt;TK&gt;
    for (const k in te.table) {
      result[k] = new ColumnExpression(te, te.table[k])
    }
    return result
  }
  
  let tq1 = query(ee1)
  console.log(tq1.name.eq('Pancakes')) // e1.name = "Pancakes"
  </code></pre>
  
  <p>This code compiles and works as expected. What I'm getting stuck on is how to make the <code>eq()</code> method leverage the generic type used by Column. I can easily extend ColumnExpression to use another type parameter, such as CT like below: </p>
  
  <pre><code>class ColumnExpression&lt;TK extends Table, CT, CK extends Column&lt;CT&gt;&gt; {
  ...
    eq(val: CT) { ... }
  }
  </code></pre>
  
  <p>That part makes sense. The problem then is carrying that forward into the Mapped Type definition for <code>TableQuery</code>. It seems TableQuery needs to be parameterized in some way that I can pass in a CT to the ColumnExpression but I can't figure out how to do that. Any ideas? </p>