Record
Некоторые данные образуют таблицу: по одному значению на каждый вариант литерального объединения. Когда герой побеждает монстра, он получает очки опыта (XP), и сколько их, зависит от типа монстра:
type MonsterType = "wolf" | "troll" | "dragon";
const xpFor = {
wolf: 15,
troll: 40,
};Кто-то забыл про дракона. TypeScript не может знать, что в этом объекте должен быть ключ для каждого MonsterType, пока вы ему не скажете. Для этого есть готовый тип Record:
type MonsterType = "wolf" | "troll" | "dragon";
const xpFor: Record<MonsterType, number> = {
wolf: 15,
troll: 40,
};main.ts(3,7): error TS2741: Property 'dragon' is missing in type '{ wolf: number; troll: number; }' but required in type 'Record<MonsterType, number>'.Record<MonsterType, number> означает «объект ровно с ключами из MonsterType и числом для каждого из них». Типу Record вы передаёте два типа в угловых скобках: сначала тип ключей, затем тип значений. (Как работают типы с угловыми скобками и как писать свои, вы узнаете в уроке «Обобщения».) Теперь TypeScript проверяет таблицу. Пропущенный ключ даёт TS2741, как выше. Ключ с опечаткой, например dragn: 100, даёт:
main.ts(6,5): error TS2561: Object literal may only specify known properties, but 'dragn' does not exist in type 'Record<MonsterType, number>'. Did you mean to write 'dragon'?А значение не того типа, например wolf: "15", даёт TS2322.
Таблицу читают с помощью квадратных скобок, как вы читали stock[fruit] в курсе JavaScript:
type MonsterType = "wolf" | "troll" | "dragon";
type Monster = { name: string; type: MonsterType };
const xpFor: Record<MonsterType, number> = {
wolf: 15,
troll: 40,
dragon: 100,
};
const grak: Monster = { name: "Грак", type: "troll" };
console.log(`${grak.name} даёт ${xpFor[grak.type]} XP`);Программа выведет:
Грак даёт 40 XPТип grak.type — MonsterType, поэтому TypeScript знает, что такой ключ есть, и результат — number. Если ключ — просто любой текст, чтение будет ошибкой, потому что этого текста может вовсе не быть среди ключей. Поставьте эти две строки сразу под }; таблицы, на строки 9 и 10:
const key: string = "troll";
console.log(xpFor[key]);main.ts(10,13): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Record<MonsterType, number>'.
No index signature with a parameter of type 'string' was found on type 'Record<MonsterType, number>'.Сообщение говорит, что TypeScript не может использовать string для чтения из Record<MonsterType, number>, поэтому у результата был бы тип any. Исправить это можно, дав ключу правильный тип — MonsterType.
Record удобен и для подсчёта. Начните с 0 у каждого ключа и прибавляйте единицу в цикле:
type MonsterType = "wolf" | "troll" | "dragon";
type Monster = { name: string; type: MonsterType };
const defeated: Monster[] = [
{ name: "Грак", type: "troll" },
{ name: "Клык", type: "wolf" },
{ name: "Тень", type: "wolf" },
];
const counts: Record<MonsterType, number> = { wolf: 0, troll: 0, dragon: 0 };
for (const monster of defeated) {
counts[monster.type]++;
}
console.log(counts);Программа выведет:
{ wolf: 2, troll: 1, dragon: 0 }Задание
- 1
Задайте
pricesтипRecord<Rarity, number>. TypeScript сообщит о недостающей цене — добавьте её. Легендарный предмет стоит200золотых. - 2
Выведите цену каждого предмета из инвентаря, используя
pricesи редкость предмета. Программа выведет:Верёвка: цена 10 Серебряный меч: цена 50 Драконье яйцо: цена 200 Факел: цена 10 - 3
Подсчитайте предметы каждой редкости в
const counts: Record<Rarity, number>и выведите результат в конце:Верёвка: цена 10 Серебряный меч: цена 50 Драконье яйцо: цена 200 Факел: цена 10 Обычных: 2, редких: 1, легендарных: 1