Что такое Record в TypeScript и как его использовать?
Что проверяют
Знаете ли вы утилитные типы и понимаете ли, что Record даёт совершенно разные гарантии в зависимости от того, чем параметризован ключ. За вопросом стоит умение типизировать словари и таблицы соответствий, а не только пропсы компонента.
Как отвечать
Record<K, T> это встроенный маппед-тип вида { [P in K]: T }: он строит объект, ключи которого берутся из K, а значения имеют тип T.
Практический смысл зависит от K:
type Status = "new" | "active" | "banned";
// все ключи обязательны: забыли вариант, компилятор сразу покажет
const labels: Record<Status, string> = {
new: "Новая",
active: "Активная",
banned: "Заблокирована",
};
// словарь с произвольными ключами
const counters: Record<string, number> = {};Первый случай и есть главная польза: добавили вариант в объединение, и все таблицы подписей, обработчиков и цветов перестают собираться, пока их не дополнили. Второй случай просто индексная сигнатура, никакой проверки состава ключей там нет.
Рядом полезны Partial<Record<Status, string>>, когда заполнены не все ключи, и Readonly для констант. В роли ключа годятся строковые и числовые литералы, их объединения и keyof другого типа.
Где спотыкаются
Ждут защиты от отсутствующего ключа. У Record<string, T> обращение по любому ключу даёт тип T, хотя значения может не быть, и undefined прилетает уже в рантайме. Лечится флагом noUncheckedIndexedAccess или явным Record<string, T | undefined>.
Перебирают ключи через Object.keys и удивляются типу string[] вместо объединения литералов. Так задумано: объект в рантайме может содержать лишние поля. Нужен перебор по списку литералов, а приведение типа кандидат должен уметь обосновать.
Берут Record<string, any> вместо описания формы данных. Такой тип компилируется и не проверяет ничего; для ответа сервера нужен интерфейс или схема с разбором, а не словарь с произвольным значением.