TypeScript 2.8 条件类型问题

TypeScript 2.8 Conditional Type Issue

在遇到 TS 2.6 中的问题后,我一直在试验 TypeScript 2.8 的条件类型,但我 运行 遇到了一个我不理解的错误,需要一些帮助:

这个例子涉及到这个问题。

export class Column<T> {
  readonly base: T

  constructor(base: T) {
    this.base = base
  }
}

export class ColumnExpression<E, CT, CK extends Column<CT>> {
  readonly table: TableExpression<E>
  readonly column: CK
  alias?: string

  constructor(table: TableExpression<E>, column: CK, alias?: string) {
    this.table = table
    this.column = column
    this.alias = alias
  }
}

type Table<E> = {
  [P in keyof E]: E[P] extends (infer U) ? Column<U> : never
}

type TableQuery<E> = {
  [P in keyof E]: E[P] extends Column<(infer CT)> ? ColumnExpression<E, CT, E[P]> : never
}

export class TableExpression<E> {
  readonly table: Table<E>
  alias?: string

  constructor(table: Table<E>, alias?: string) {
    this.table = table
    this.alias = alias
  }
}

function toTable<E>(target: E): Table<E> {
  let result = {} as Table<E>
  for (const k in target) {
    result[k] = new Column(target[k])
  }
  return result
}

function toTableExpression<E>(target: E): TableExpression<E> {
  const table = toTable(target)
  return new TableExpression(table)
}

function toTableFilter<E>(target: TableExpression<E>): TableQuery<E> {
  let result = {} as TableQuery<E>
  let table = target.table
  for (const k in table) {
    result[k] = new ColumnExpression(target, table[k])
  }
  return result
}

class Test {
  id: number
  name: string
  createdAt: Date
  createdById: number
}

let contentTable = toTable(new Test())
let contentFilter = toTableFilter(new TableExpression(contentTable))

contentTable.id.base
contentTable.name.base

第 56 行弹出确切的错误:

result[k] = new ColumnExpression(target, table[k])

错误:

(56,46): error TS2345: Argument of type 'Table<E>[keyof E]' is not assignable to parameter of type 'Column<{}>'.
  Type 'Column<E[keyof E]>' is not assignable to type 'Column<{}>'.
    Type 'E[keyof E]' is not assignable to type '{}'.
1:57:46 PM - Compilation complete. Watching for file changes.

在 TS 2.6 中,我根据索引类型访问(根据其他人对 SO 的建议)为 ColumnExpression 使用了不同的类型定义,作为参考,它被定义为:

type TableQuery<TK extends TableLike> = {
  [P in keyof TK]: Column.ColumnExpression<TK, TK[P]['base'], TK[P]>
}

TK[P]['base'] 允许我访问基础列类型,但这种方法从 TS 2.7 开始不再有效。目前还不清楚它是否是一个错误。我已经通读了一些看似相关但 none 解决这个确切问题的 GH 问题。我希望 2.8 中引入的条件类型能让我更干净地解决这个问题,但到目前为止,运气并不好。

如有任何想法,我们将不胜感激。

正如我所说,您的类型似乎已损坏,我不知道它们应该是什么。 如果您的代码在运行时工作(是吗?)那么下面的代码不会产生错误,似乎与您在运行时所做的一致,并且不使用条件类型(所以应该使用 TS2.6):

export class Column<T> {
  readonly base: T

  constructor(base: T) {
    this.base = base
  }
}

// remove CT type, it can be calculated as CK['base'] if needed
export class ColumnExpression<E, CK extends Column<any>> {
  // type CT = CK['base']
  readonly table: TableExpression<E>
  readonly column: CK
  alias?: string

  constructor(table: TableExpression<E>, column: CK, alias?: string) {
    this.table = table
    this.column = column
    this.alias = alias
  }
}

// no need for conditional types, reduces to this:
type Table<E> = {
  [P in keyof E]: Column<E[P]>
}

// no need for conditional types, reduces to this:
type TableQuery<E extends Table<any>> = {
  [P in keyof E]: ColumnExpression<E, E[P]>
}


export class TableExpression<E> {
  readonly table: Table<E>
  alias?: string

  constructor(table: Table<E>, alias?: string) {
    this.table = table
    this.alias = alias
  }
}

function toTable<E>(target: E): Table<E> {
  let result = {} as Table<E>
  for (const k in target) {
    result[k] = new Column(target[k])
  }
  return result
}

function toTableExpression<E>(target: E): TableExpression<E> {
  const table = toTable(target)
  return new TableExpression(table)
}

// this is the real output of toTableFilter();
// note the difference between TableThing<E> and TableQuery<E>:
type TableThing<E> = {
  [P in keyof E]: ColumnExpression<E, Column<E[P]>>
}


function toTableFilter<E>(target: TableExpression<E>): TableThing<E> {
  let result = {} as TableThing<E>;
  let table = target.table
  for (const k in table) {
    const z = new ColumnExpression(target, table[k])
    result[k] = z
  }
  return result
}

class Test {
  id!: number
  name!: string
  createdAt!: Date
  createdById!: number
}

let contentTable = toTable(new Test())
let contentFilter = toTableFilter(new TableExpression(contentTable))

contentTable.id.base
contentTable.name.base

所有类型检查都应该发出与您问题中有问题的代码相同的 JavaScript。老实说,我不确定代码是否在运行时执行了它应该执行的操作,或者 TableThing<E> 实际上 意味着 。我认为这可能取决于您自己的理解。

希望对您有所帮助。祝你好运!