【问题标题】:TypeScript: Using Mapped Types with GenericsTypeScript:使用泛型映射类型
【发布时间】:2018-06-25 22:03:00
【问题描述】:

我一直在尝试使用 TypeScript,希望能创建一个类型安全的数据库查询库(大致基于 Scala 的 Slick)。在 Mapped Types 的帮助下,我取得了一些不错的进展,但我一直坚持保留 Column 的基础类型。请参阅下面的代码以获取全面的示例:

class Table {}

class TableExpression<TK extends Table> {
  table: TK
  alias: string
  constructor(table: TK, alias?: string) {
    this.table = table
    this.alias = alias || table.constructor.name
  }
}

class Column<T> {
  name: string
  defaultValue?: T
}

class StringC extends Column<string> {
  constructor(name: string, defaultValue?: string) {
    super()
    this.name = name
    this.defaultValue = defaultValue
  }
}

class ColumnExpression<TK extends Table, CK extends Column<any>> {
  table: TableExpression<TK>
  column: CK
  alias: string
  constructor(table: TableExpression<TK>, 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<TK extends Table> = {
  [P in keyof TK]: ColumnExpression<TK, TK[P]>
}

function query<TK extends Table>(te: TableExpression<TK>): TableQuery<TK> {
  let result = {} as TableQuery<TK>
  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"

此代码按预期编译和工作。我遇到的问题是如何使 eq() 方法利用 Column 使用的泛型类型。我可以轻松扩展 ColumnExpression 以使用其他类型参数,例如 CT,如下所示:

class ColumnExpression<TK extends Table, CT, CK extends Column<CT>> {
...
  eq(val: CT) { ... }
}

这部分是有道理的。然后问题是把它带到TableQuery 的映射类型定义中。似乎 TableQuery 需要以某种方式参数化,以便我可以将 CT 传递给 ColumnExpression,但我不知道该怎么做。有任何想法吗?

【问题讨论】:

    标签: typescript generics mapped-types


    【解决方案1】:

    您可以使用Lookup Types

    要在eq 中将泛型类型作为参数值更改您的签名,如下所示:

    eq(val: CK['defaultValue']): string
    

    对于Column&lt;string&gt;,这将导致string | undefined,至少启用--strictNullChecks。那是因为defaultValue 是可选的。所以调用eq(undefined) 仍然有效。

    您可以通过 CK['defaultValue'] &amp; Object 删除 undefined,但我不确定这是否有任何我不知道的副作用。

    【讨论】:

    • 该解决方案似乎有效。我向Column&lt;T&gt;base: T 添加了一个附加值,然后在base 上使用类型查找来获取T
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-11
    • 1970-01-01
    • 2021-03-28
    • 2023-04-04
    • 1970-01-01
    • 2014-12-09
    • 1970-01-01
    相关资源
    最近更新 更多