【问题标题】:TypeScript 2.8 Conditional Type IssueTypeScript 2.8 条件类型问题
【发布时间】:2018-09-24 21:12:46
【问题描述】:

在遇到 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 开始不再适用。目前尚不清楚这是否是一个错误。我已经阅读了一些似乎相关但没有解决这个确切问题的 GH 问题。我希望 2.8 中引入的条件类型能让我更干净地解决这个问题,但到目前为止,运气不佳。

任何想法将不胜感激。

【问题讨论】:

  • 你的类型很混乱; contentFilter 是无意义的类型TableQuery&lt;Test&gt;,一盒never。如果您的意思是 TableQuery&lt;Table&lt;Test&gt;&gt;,那么您的 toTableFilter() 定义是错误的。请仔细检查您的类型,并可能添加有关类型应该是什么的 cmets?也许类型参数名称有一些一致性? (E 在某些地方似乎是“任何类型”,而在其他地方则是“Column 属性的记录”)。现在,每当我开始解决您的问题(类型参数推断失败)时,TypeScript 都会立即抱怨其他问题。

标签: typescript mapped-types conditional-types


【解决方案1】:

正如我所说,您的类型似乎被破坏了,我不知道它们应该是什么。 如果您的代码在运行时工作(是吗?),那么以下代码不会产生错误,似乎与您在运行时所做的一致,并且不使用条件类型(因此应该与 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&lt;E&gt; 实际上意味着。我想这可能取决于你自己想办法。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-14
    • 2016-04-04
    • 1970-01-01
    • 2020-03-29
    • 2019-03-21
    • 1970-01-01
    • 2021-03-24
    • 2018-10-06
    相关资源
    最近更新 更多