首页 > 解决方案 > 未在接口中推断的打字稿泛型函数参数

问题描述

我正在尝试构建一个表接口,该接口提供对表列具有的值的严格键入以及对列值执行并返回字符串的函数。但是,我在函数参数中使用泛型时遇到问题。下面是我正在做的一个简单示例。我正在使用 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
}

我目前在列对象上遇到错误,说明...

类型 'ColumnInfo<RowImpl, "id">' 不可分配给类型 'ColumnInfo<RowImpl, "id" | "c1" | "c2">'。输入'"id" | "c1" | "c2"' 不能分配给类型 '"id"'。类型 '"c1"' 不可分配给类型 '"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] 两个示例都应该有效,但函数参数不是。有人可以解释我的问题是什么以及我需要更改什么才能获得工作界面吗?

谢谢

标签: typescriptinterfacetypescript-generics

解决方案


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

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

这里keyof T是 的所有T,而不仅仅是一个特定的键。

当您尝试将更具体的实例分配给更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"'
}

打字稿游乐场链接


推荐阅读