TypeScript typed comparators and multi-key sorting
Array.prototype.sort takes a comparator, a function that returns a negative number, zero, or a positive number. Writing the same comparison logic repeatedly is error-prone. A helper like by(key) builds a comparator for any property of T, and the constraint K extends keyof T makes the compiler reject a property that does not exist.
TypeScript
interface Employee {
name: string;
dept: string;
salary: number;
}
type Comparator<T> = (a: T, b: T) => number;
function by<T, K extends keyof T>(key: K, direction: "asc" | "desc" = "asc"): Comparator<T> {
const sign = direction === "asc" ? 1 : -1;
return (a, b) => (a[key] < b[key] ? -sign : a[key] > b[key] ? sign : 0);
}
function thenBy<T>(...comparators: Comparator<T>[]): Comparator<T> {
return (a, b) => {
for (const compare of comparators) {
const result = compare(a, b);
if (result !== 0) {
return result;
}
}
return 0;
};
}
const staff: Employee[] = [
{ name: "Ada", dept: "Eng", salary: 120 },
{ name: "Bo", dept: "Ops", salary: 90 },
{ name: "Cy", dept: "Eng", salary: 100 },
{ name: "Di", dept: "Ops", salary: 90 },
];
const sorted = [...staff].sort(thenBy<Employee>(by("dept"), by("salary", "desc"), by("name")));
console.log(sorted.map((e) => e.dept + ":" + e.name + ":" + e.salary).join(" "));
console.log(staff.map((e) => e.name).join(" "));