TypeScript - уникальность поля `id` каждого объекта в массиве
Возможно ли как-то TypeScript'ом задать такое ограничение (некий кастомный тип), чтобы в каждом объекте массива поле id содержало уникальное значение?
Валидный массив:
[{id: 1}, {id: 2}, {id: 3}]
Невалидный массив:
[{id: 1}, {id: 1}, {id: 2}]
Если такое возможно сделать, то как?
Ответы (1 шт):
От части возможно, при следующих условиях:
- Массив имеет тип кортежа
- Тип этого поля в каждом объекте должен быть литералом, то есть например
1вместоnumber - Тип будет проверяться функцией, при простом присваивании такое не проверить
Для начала определим тип для элементов кортежа:
type Identifiable<I extends number> = { id: I } // Предположим что id всегда число
Теперь напишем проверку уникальности элементов:
type IsDistinct<T extends readonly Identifiable<number>[], E = never> =
T extends readonly [
Identifiable<infer I>,
...infer Rest extends Identifiable<number>[]
]
? I extends E
? false
: IsDistinct<Rest, E | I>
: true
Здесь мы рекурсивно проходим по кортежу и проверяем есть ли тип id элемента в юнионе E. Если его там нет, добавляем его туда и идём дальше, иначе возвращаем false. Когда доходим до конца возвращаем true.
Все массивы помечены как
readonlyдля корректной работы сas const
Далее напишем функцию которая будет проверять тип значения:
// Вспомогательный тип для аргументов функции
type DistinctOnly<T extends readonly Identifiable<number>[]> = IsDistinct<T> extends true ? T : never
function ensureDistinct<T extends readonly Identifiable<number>[]>(arg: DistinctOnly<T>): T {
return arg;
}
Проверим функцию на ваших примерах:
// Работает
ensureDistinct([{id: 1}, {id: 2}, {id: 3}])
// Тоже работает, потому что тип id выводится как number -> добавим as const
ensureDistinct([{id: 1}, {id: 1}, {id: 2}])
ensureDistinct([{id: 1}, {id: 2}, {id: 3}] as const) // OK
ensureDistinct([{id: 1}, {id: 1}, {id: 2}] as const) // Ошибка!