(我将关注你的Column
个例子,忽略Foo
,因为推理问题是不同的,你关心的是Column
而不是Foo
).
从概念上讲,像"一个对象集合,对于我知道的特定D
,每个对象的类型都是Column<D, V>
,但对于一些我不关心的V
",需要existentially quantified generic types.(我想你需要知道D
,因为如果你不知道,就没有办法打电话给accessorFunction
).
但是很少有语言支持这种类型,TypeScript也不支持;有关相关功能请求,请参阅microsoft/TypeScript#14466.如果我们真的有,你可以说Array<Column<D, exists V>>
.但我们没有,所以我们不能.
有一些方法可以对存在类型进行编码.general方法使用类似Promise
的数据 struct 来反转具有generic个回调的控制流.泛型函数允许调用者指定类型参数,而实现者只知道它是some类型.看起来是这样的:
type SomeColumn<D> = <R>(cb: <V>(col: Column<D, V>) => R) => R;
SomeColumn<D>
就像Promise<Column<D, ??>>
的then()
法.它接受回调,然后大概调用它所持有的底层Column<D, V>
的回调.要将Column<D, V>
转换为SomeColumn<D>
,我们可以使用辅助函数:
const someColumn = <D, V>(col: Column<D, V>): SomeColumn<D> => cb => cb(col);
然后,您的数组可以如下所示:
const columns = [
someColumn({
accessorFunction: (a: string) => "test",
cell: (info) => info.toUpperCase()
}),
someColumn({
accessorFunction: (a: string) => a.length,
cell: info => info.toFixed(2)
})
];
那columns
是SomeColumn<string>
型的.如果我们想处理数组,我们必须添加一个嵌套回调.比如说:
const results = columns.map(
sc => sc(
col => col.cell(col.accessorFunction("hello"))
)
)
// const results: any[]
console.log(results); // ["TEST", "5.00"]
注意,我正在做的是我能用Column<string, V>
做的唯一有用的事情,我不知道V
...也就是说,拨打col.cell(col.accessorFunction(someString))
.无论如何,results
是一个由cell()
个输出组成的数组(您输入的是any
,所以我们有any[]
个).
这对于您的用例来说是过度的.也许你们所关心的只是cell()
方法输入的推断,你们不介意columns
是一个类似Array<Column<string,string> | Column<string, number>>
的array.如果是这样,您可以保留helper函数,但只需go 掉其中任何类似Promise
的行为.它接受Column<D, V>
并返回Column<D, V>
:
const column = <D, V>(col: Column<D, V>) => col;
const columns = [
column({
accessorFunction: (a: string) => "test",
cell: (info) => info.toUpperCase()
}),
column({
accessorFunction: (a: string) => a.length,
cell: info => info.toFixed(2)
})
]; // okay
但您会发现很难通过编程方式处理这些问题:
const results = columns.map(
col => col.cell(col.accessorFunction("hello")) // error!
// -----------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// compiler doesn't know that this will always be the right type for col.cell
);
这里也有一些变通方法,但它们很烦人,如果小心正确地执行,您还可以使用类型断言:
const results = (columns as Array<Column<string, any>>).map(
col => col.cell(col.accessorFunction("hello"))
);
Playground link to code