Reputation: 157991
I have the following TypeScript, which does work:
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"));
}
The idea is you pass in an array of objects, and a column object where the keys are the headers (can be any string) and the value should be either the name of a property or a function returning a value.
const users = [{id: 1, name: 'Alice', isCool: true}, ...];
const columns = {
'Id': 'id,
'Name': 'name',
'Is cool': u => u.isCool ? 'Yes' : 'No',
};
exportToCsv(users, columns);
This all works, but I'd like stricter typing. The following "works", with the exception that I just can't figure out how to write the Columns
type generically. Keep getting stuff not being assignable, type parameters being declared but not used, etc, etc.
interface Columns<T> {
[s: string]: ?;
}
const exportAsCsv = function <T> (data: T[], columns: Columns<T>): void
How do I express this properly?
Upvotes: 1
Views: 112
Reputation: 249536
You can ensure the value of Columns
is either a key of T
or a function accepting T
using this type:
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
});
You can also create the columns separately from the call but you need to specify 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);
Upvotes: 1