如何在TypeScript中表达“以T为参数的函数|字符串,它是T的键”

时间:2018-11-20 11:39:11

标签: typescript

我有以下可以正常工作的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

我该如何正确表达?

1 个答案:

答案 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);