TypeScript: type-safe select, where, and orderBy
A relational query filters rows, sorts them, and chooses columns. In TypeScript a table can be an array of objects, and the type of the row describes its columns. The generic constraint K extends keyof T says that a column name must be one of the row type's property names, so a misspelled column is a compile-time error instead of an empty result at run time.
TypeScript
type Row = { id: number; name: string; dept: string; salary: number };
const table: Row[] = [
{ id: 1, name: "Ada", dept: "Eng", salary: 120 },
{ id: 2, name: "Bo", dept: "Ops", salary: 90 },
{ id: 3, name: "Cy", dept: "Eng", salary: 100 },
{ id: 4, name: "Di", dept: "Ops", salary: 80 },
];
function where<T>(rows: T[], predicate: (row: T) => boolean): T[] {
return rows.filter(predicate);
}
function orderBy<T, K extends keyof T>(rows: T[], key: K): T[] {
return [...rows].sort((a, b) => (a[key] < b[key] ? -1 : a[key] > b[key] ? 1 : 0));
}
function select<T, K extends keyof T>(rows: T[], ...keys: K[]): Pick<T, K>[] {
return rows.map((row) => {
const picked = {} as Pick<T, K>;
for (const key of keys) {
picked[key] = row[key];
}
return picked;
});
}
const wellPaid = where(table, (row) => row.salary >= 100);
const sorted = orderBy(wellPaid, "name");
console.log(JSON.stringify(select(sorted, "name", "salary")));
console.log(JSON.stringify(select(orderBy(table, "salary"), "name")));