typescript: шаблонная функция в которой в качестве параметра выступает название поля объекта

у меня есть множество однотипных функций для сортировки, которые передаются фреймворку, отрисовывающему таблицы и сортирующему поля (Ant Desgin) и отличающуюся названием полей и типом данных:

sorter: (a: IType1, b: IType1) => (a.created_at ?? 0) - (b.created_at ?? 0),

хочется вместо кода сравнения двух значений указывать какую-то более короткую запись, не зависящую от типа данных, что-то типа

sorter: my_sorter,

Если написать функцию

function my_sorter(a: IType1, b: IType1) {
    return (a.created_at ?? 0) - (b.created_at ?? 0);
}

То это то, что надо за исключением 2х вещей - одна применима только для заданного типа и заданного названия поля, а хотелось бы указывать функцию и дополнительно указывать поля и типы данных (если нужно), что-то типа

sorter: my_sorter<IType1.created_at>,

Можно ли это реализовать в typescript?

P.S.

Не очень бы хотелось указывать поля в виде строк и т.д., т.е. легко можно написать функцию, чтобы делать так:

sorter: (a: IType1, b: IType1) => my_sorter(a, b, 'created_at'),

Но это уже как-то менее эстетично :(


Ответы (1 шт):

Автор решения: MoloF

Для реализации функции сортировки которая будет принимать ключ и возвращать отсортированный массив можно воспользоваться типом который отсечёт все лишние ключи не содержащие число и сделать функцию с замыканием которая будет принимать тот самый ключ и возвращать результат для функции sort

Рабочий пример: typescriptlang.org

type NumericKeys<T> = {
  [K in keyof T]: T[K] extends number ? K : never;
}[keyof T];

function compareByKey<T extends object>(key: NumericKeys<T>, customValidation?: (a: number, b: number) => number) {
  return (a: T, b: T) => {
    if (customValidation) {
      return customValidation(Number(a[key]), Number(b[key]));
    } else {
      return Number(a[key]) - Number(b[key]);
    }
  };
}

//

interface User {
  name: string;
  created_at: number;
}

interface Car {
  id: number;
  model: string;
}

const users: User[] = [
  { name: 'test 1', created_at: 1 },
  { name: 'test 2', created_at: 2 },
  { name: 'test 3', created_at: 3 },
];


const cars: Car[] = [
  { id: 1, model: 'test 1' },
  { id: 2, model: 'test 2' },
  { id: 3, model: 'test 3' },
];

users.sort(compareByKey<User>('created_at')); // [1, 2, 3]
users.sort(compareByKey<User>('created_at', (a, b) => (b - a))); // [3, 2, 1]

cars.sort(compareByKey<Car>('id')); // [1, 2, 3]
cars.sort(compareByKey<Car>('id', (a, b) => (b - a))); // [3, 2, 1]
→ Ссылка