我有以下可以正常工作的TypeScript:
interface Columns {
[s: string]: string | ((item: any) => string);
}
const exportAsCsv = function (data: any[], columns: Columns): void {
const header = Object.keys(columns)
.map(x => `"${x}"`)
.join(";");
const rows = [];
for (const item of data) {
const row = Object
.values(columns)
.map(field => typeof field === 'function' ? field(item) : item[field])
.map(x => (x || '').replace(/"/, '""'))
.map(x => `"${x}"`)
.join(";");
rows.push(row);
}
console.log([header, ...rows].join("\r\n"));
}
这个想法是您传递一个对象数组和一个列对象,其中键是标题(可以是任何字符串),并且值应该是属性名称或返回值的函数。
const users = [{id: 1, name: 'Alice', isCool: true}, ...];
const columns = {
'Id': 'id,
'Name': 'name',
'Is cool': u => u.isCool ? 'Yes' : 'No',
};
exportToCsv(users, columns);
这一切都可行,但我想更严格地输入。下面的“工作原理”,只是我无法弄清楚如何通用地编写Columns
类型。不断获取无法分配的内容,声明但未使用的类型参数等,等等。
interface Columns<T> {
[s: string]: ?;
}
const exportAsCsv = function <T> (data: T[], columns: Columns<T>): void
我该如何正确表达?
答案 0 :(得分:1)
使用以下类型,可以确保Columns
的值是T
的键或接受T
的函数:
interface Columns<T> {
[s: string]: keyof T | ((item: T) => string);
}
const exportAsCsv = function <T>(data: T[], columns: Columns<T>): void {
//...
}
const users = [{ id: 1, name: 'Alice', isCool: true }];
exportAsCsv(users, {
'Id': 'id',
'Name': 'name',
'Is cool': u => u.isCool ? 'Yes' : 'No',
});
exportAsCsv(users, {
'Id': 'id',
'Name': 'name2', // error
'Is cool': u => u.isCool ? 'Yes' : 'No', //error
});
您还可以与调用分开创建列,但是需要指定T
:
const users = [{ id: 1, name: 'Alice', isCool: true }];
const columns : Columns<typeof users[number]> = {
'Id': 'id',
'Name': 'name',
'Is cool': u => u.isCool ? 'Yes' : 'No',
};
exportAsCsv(users, columns);