未在接口中推断出打字稿通用函数参数

typescript generic function parameters not inferred in interface

我正在尝试构建一个 table 接口,该接口提供对 table 列所具有的值的严格键入以及对列值和 returns 字符串执行的函数.但是,我在函数参数中使用泛型时遇到问题。下面是我正在做的一个简单例子。我正在使用 Typescript 3.9.5

/**
 * Interface for of a single column
 */
interface ColumnInfo<T, K extends keyof T> {
    column: K,
    cellFactory: (value: T[K]) => string
}

/**
 * Interface for all table columns. Only single row for debugging purposes.
 */
interface Columns<T> {
    columns: ColumnInfo<T, keyof T>;
};

/**
 * Row implementation
 */
type RowImpl = {
    id: string; 
    c1: string; //Could be number, Date, etc, simplified for now
    c2: string;
}

/**
 * id column implementation
 */
const columnInfo: ColumnInfo<RowImpl, 'id'> = {
    column: 'id',
    cellFactory: () => ''
}

/**
 * Table columns Implementation. Currently only accepting a single column for debugging
 */
const columns: Columns<RowImpl> = {
    columns: columnInfo
}

我目前在列对象上收到一条错误,指出...

Type 'ColumnInfo<RowImpl, "id">' is not assignable to type 'ColumnInfo<RowImpl, "id" | "c1" | "c2">'. Type '"id" | "c1" | "c2"' is not assignable to type '"id"'. Type '"c1"' is not assignable to type '"id"'.ts(2322)

我不确定为什么在我使用 T[K] 时尝试将联合分配给特定类型。我将 cellFactory 对象简化为 T[K] 以缩小问题范围,但它现在有效。

interface ColumnInfo<T, K extends keyof T> {
    column: K,
    cellFactory: T[K]
}


/**
 * Interface for all table columns. Only single row for debugging purposes
 */
interface Columns<T> {
    columns: ColumnInfo<T, keyof T>;
};

/**
 * Row implementation
 */
type RowImpl = {
    id: string;
    c1: number;
    c2: string;
}

/**
 * id column implementation
 */
const columnInfo: ColumnInfo<RowImpl, 'id'> = {
    column: 'id',
    cellFactory: ''
}

/**
 * Table columns Implementation. Currently only accepting a single column
 */
const columns: Columns<RowImpl> = {
    columns: columnInfo
}

将 cellFactory 作为 T[K] 而不是 (val: T[K]) => string 就可以了。为什么是这样?我假设它是否可以推断出 T[K] 这两个示例都应该有效,但函数参数无效。有人可以解释我的问题是什么以及我需要更改什么才能获得工作界面吗?

谢谢

当您尝试定义可用于多个不同列的对象时,问题开始出现。

interface Columns<T> {
    columns: ColumnInfo<T, keyof T>;
};

这里的keyof TT所有个键,不是一个特定的键。

当您尝试将 columnInfo: ColumnInfo<RowImpl, 'id'> 等更具体的实例分配给更广泛的类型 ColumnInfo<RowImpl, keyof RowImpl> 时,column 属性 很好,因为 id可分配给 keyof RowImpl.

问题出在回调上。更广泛的类型 ColumnInfo<RowImpl, keyof RowImpl> 需要一个回调,该回调采用 RowImpl 对象的所有可能值,但您为其提供了一个只能接受一种特定类型的函数。所以 cellFactory: (value: RowImpl['id']) => string 不能分配给 cellFactory: (value: RowImpl[keyof RowImpl]) => string.

如果您要扩大 ColumnInfo 以便 value 是对象的任何值,错误就会消失。但我不推荐这样做,因为我们会丢失有关特定列类型的信息。

interface ColumnInfo<T, K extends keyof T> {
    column: K,
    cellFactory: (value: T[keyof T]) => string
}

我自己处理过这个特殊问题,我的解决方案是使用映射对象类型,其中 columnInfo 的键与 RowImpl 的键匹配。这允许我们声明给定键的 ColumnInfo 必须仅用于该列。我们不需要加宽到 keyof T 所以我们避免了错误。

type Columns<T> = {
    [K in keyof T]?: ColumnInfo<T, K>;
};
const columns: Columns<RowImpl> = {
    id: columnInfo, // ok
    c1: columnInfo, // error: Type '"id"' is not assignable to type '"c1"'
}

Typescript Playground Link