自动从第一个参数推断出第二个通用参数

时间:2018-08-15 12:27:49

标签: typescript

我具有以下接口来定义定义表的列:

export interface IColumnDefinition<TRow, TField extends keyof TRow> {
    field: TField;
    label?: string;
    formatter?: (value: TRow[TField], row: TRow) => string;
}

现在我只想提供行的类型(TRow,然后让TypeScript根据{{1 }}属性。

现在假设我的行具有以下界面:

TField

我尝试的是以下内容:

field

这给了我以下错误:

  

通用类型“ IColumnDefinition ”需要2个类型的参数。

我还尝试使用函数创建定义,希望可以从参数中推断出类型:

interface User {
    name: string;
    birthDate: number;
}

这给了我一个类似的错误:

  

除了2个类型参数外,但得到1个。

但是,如果我还将行作为参数包含在内,并且一起删除所有通用参数,则效果很好:

const birthDateColumnDefinition: IColumnDefinition<User> = {
    field: 'birthDate',
    formatter: value => new Date(value).toDateString(),
}

这确实有效,但这不是一个选择,因为定义列时我实际上没有一行。

那么有什么方法可以做到这一点(最好是使用接口而不是函数)?

2 个答案:

答案 0 :(得分:1)

  

现在我只想提供行的类型(TRow),并且   让TypeScript自动推断字段的类型(TField)   基于字段属性中的值。

这看起来与partial type inference非常相似,目前打字稿中尚不支持Needle design time,目前尚不清楚此特定用例是否会支持它。

但是您可以做一件事-您可以使一个函数接受TRow的显式类型参数,然后返回另一个函数,该函数将从其参数推断TField。语法有点笨拙,但可以使用:

function columnDefinition<TRow>(): 
   <TField extends keyof TRow>(def: IColumnDefinition<TRow, TField>) => 
        IColumnDefinition<TRow, TField> {
    return  <TField extends keyof TRow>(def: IColumnDefinition<TRow, TField>) => def
}

export interface IColumnDefinition<TRow, TField extends keyof TRow> {
    field: TField;
    label?: string;
    formatter?: (value: TRow[TField], row: TRow) => string;
}

interface User {
    name: string;
    birthDate: number;
}

要使用它,请不带任何参数调用columnDefinition<User>(),然后立即以列定义对象作为参数调用返回的函数:

const birthDateColumnDefinition = columnDefinition<User>()({
    field: 'birthDate',
    formatter: value => new Date(value).toDateString(), 
            // value inferred as (parameter) value: number
});

答案 1 :(得分:0)

我最终使用artem的答案编写了一个列定义构建器类:

export class ColumnDefinitionBuilder<TRow> {

  private _columns: Array<IColumnDefinitionWithField<TRow, any>> = [];

  public define<TValue>(field: ((row: TRow) => TValue) | string, def: IColumnDefinition<TRow, TValue>)
    : ColumnDefinitionBuilder<TRow> {
    this._columns.push({...def, field});
    return this;
  }

  public get columns() {
    return this._columns;
  }
}

允许使用以下语法定义列:

const userColumnDefinitions = new ColumnDefinitionBuilder<User>()
    .define(x => x.birthDate, {
        label: 'Birth date',
        formatter: x => new Date(x).toDateString(),
    })
    .define(x => x.name, {
        label: 'Name',
    })
    .columns;