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

sro*_*oes 5 typescript

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

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

现在我想要的是只提供行的类型 ( TRow),让 TypeScriptTField根据field属性中的值自动推断字段的类型 ( ) 。

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

interface User {
    name: string;
    birthDate: number;
}
Run Code Online (Sandbox Code Playgroud)

我尝试的是以下内容:

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

这给了我以下错误:

通用类型 'IColumnDefinition<TRow, TField extends keyof TRow>' 需要 2 个类型参数。

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

function createColumnDefinition<TField extends keyof TRow>(
    field: TField,
    columnDef: Partial<IColumnDefinition<TRow, TField>>): IColumnDefinition<TRow, TField>
{
    return {
        ...columnDef,
        field,
    };
}

const birthDateColumnDefinition = createColumnDefinition<User>('birthDate', {
    formatter: (value, row) => new Date(value).toDateString(),
});
Run Code Online (Sandbox Code Playgroud)

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

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

但是,如果我还将该行作为参数包含在内,并将所有通用参数一起删除,则它可以正常工作:

function createColumnDefinition<TRow, TField extends keyof TRow>(
    row: TRow,
    field: TField,
    columnDef: Partial<IColumnDefinition<TRow, TField>>): IColumnDefinition<TRow, TField>
{
    return {
        ...columnDef,
        field,
    };
}

const user: User = {
    name: 'John Doe',
    birthDate: 549064800000,
};

const birthDateColumnDefinition = createColumnDefinition(user, 'birthDate', {
    formatter: (value, row) => new Date(value).toDateString(),
});
Run Code Online (Sandbox Code Playgroud)

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

那么有什么方法可以使这项工作(最好使用接口而不是函数)?

art*_*tem 5

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

这看起来与typescript 中目前不支持的部分类型推断非常相似,目前尚不清楚这个特定用例是否会支持它。

但是你可以做一件事——你可以让一个函数接受显式类型参数 for 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 这是一个聪明的技巧,但并不是我真正希望的解决方案:-) 希望 TypeScript 有一天能够支持部分类型推断。 (2认同)