Для заданного типа определите объединение функций с параметрами ключей типа. - PullRequest
1 голос
/ 21 января 2020

У меня есть следующие типы, и я должен определить formatter с параметром типа объединения.

declare type TableHeadCell<T> = {
    key: keyof T;
    label: string;
    formatter?: (cell: T[keyof T], row?: T) => string;
};

declare type Product = {
    article: string;
    quantity: number
};

const myTHeadCell: TableHeadCell<Product> = {
   key: 'article',
   label: 'Article',
   formatter(cell: string | number, row: Product): string { /* ... */ }
}

Вместо этого мне нужен тип функции объединения, который позволит мне определить функцию с помощью: тип параметров. Было бы лучше, если бы можно было сузить до типа key.

const myTHeadCell: TableHeadCell<Product> = {
   key: 'article',
   label: 'Article',
   formatter(cell: string, row: Product): string { /* ... */ }
}

// or

const myTHeadCell: TableHeadCell<Product> = {
   key: 'quantity',
   label: 'Article',
   formatter(cell: number, row: Product): string { /* ... */ }
};

1 Ответ

3 голосов
/ 21 января 2020

Самый простой способ для этого - для TableHeadCell<T> быть типом union с одним членом для каждого K в keyof T. Один из способов добиться этого - создать сопоставленный тип, в котором каждое свойство соответствует каждому K, а затем получить объединение этих свойств:

type TableHeadCell<T> = { [K in keyof T]: {
  key: K;
  label: string;
  formatter?: (cell: T[K], row?: T) => string;
} }[keyof T];

Теперь вы можете написать следующее без ошибок:

const myTHeadCell1: TableHeadCell<Product> = {
  key: 'article',
  label: 'Article',
  formatter(cell: string, row?: Product): string { return "" }
}

const myTHeadCell2: TableHeadCell<Product> = {
  key: 'quantity',
  label: 'Article',
  formatter(cell: number, row?: Product): string { return "" }
};

Компилятор даже запомнит, что myTHeadCell1.formatter() принимает параметр string, а myTHeadCell2.formatter() принимает number, поскольку анализ типа потока управления временно сузит тип объединенное значение, основанное на присваивании:

if (myTHeadCell1.formatter) {
  myTHeadCell1.formatter(""); // okay
  myTHeadCell1.formatter(123); // error
}
if (myTHeadCell2.formatter) {
  myTHeadCell2.formatter(""); // error
  myTHeadCell2.formatter(123); // okay
}

Обратите внимание, что я изменил реализации средства форматирования, чтобы row был необязательным. Это необходимо, чтобы соответствовать определению TableHeadCell<T>. Если вы хотите, чтобы параметр row был обязательным в реализации, он должен быть обязательным для аннотированного типа, потому что вам должно быть разрешено вызывать TableHeadCell<T>.formatter!() с одним аргументом в соответствии с определением. (Убедитесь, что вы понимаете различие между типом функции с необязательными параметрами и реализацией функции, которая игнорирует дополнительные параметры, как описано в FAQ по TypeScript ).

Хорошо, надеюсь, это поможет; удачи!

Детская площадка ссылка на код

...