compareBy<T>() multi-key sort comparator

TypeScript
export type Direction = 'asc' | 'desc';

export interface SortKey<T> {
  value: (row: T) => string | number | Date | null | undefined;
  direction?: Direction;
  nullsLast?: boolean;
}

/** Build a stable multi-key comparator with null placement per key. */
export function compareBy<T>(keys: readonly SortKey<T>[]): (a: T, b: T) => number {
  return (a, b) => {
    for (const key of keys) {
      const left = key.value(a);
      const right = key.value(b);
      const nullsLast = key.nullsLast ?? true;
      if (left == null || right == null) {
        if (left == null && right == null) continue;
        return left == null ? (nullsLast ? 1 : -1) : nullsLast ? -1 : 1;
      }
      let delta: number;
      if (left instanceof Date && right instanceof Date) {
        delta = left.getTime() - right.getTime();
      } else if (typeof left === 'string' && typeof right === 'string') {
        delta = left.localeCompare(right, undefined, { sensitivity: 'base', numeric: true });
      } else if (typeof left === 'number' && typeof right === 'number') {
        delta = left - right;
      } else {
        delta = String(left).localeCompare(String(right));
      }
      if (delta !== 0) return key.direction === 'desc' ? -delta : delta;
    }
    return 0;
  };
}

// rows.sort(compareBy([{ value: (r) => r.team }, { value: (r) => r.score, direction: 'desc' }]));