Типы из значений
Обычно вы сначала пишете тип, а потом значения, которые ему подходят. Иногда бывает наоборот: сначала значение, а тип должен следовать за ним. Каждый новый герой начинает с одинаковых характеристик, записанных один раз в объекте:
const startingStats = { strength: 5, agility: 5, magic: 5 };Функциям, которые работают с характеристиками, нужен для них тип. Можно было бы написать { strength: number; agility: number; magic: number } вручную, но тогда его придётся менять при каждом изменении объекта. TypeScript умеет взять тип из значения:
const startingStats = { strength: 5, agility: 5, magic: 5 };
type Stats = typeof startingStats;
const mageStats: Stats = { strength: 2, agility: 4, magc: 9 };main.ts(4,53): error TS2561: Object literal may only specify known properties, but 'magc' does not exist in type '{ strength: number; agility: number; magic: number; }'. Did you mean to write 'magic'?Stats — это тип, который TypeScript вывел для startingStats, { strength: number; agility: number; magic: number; }, и именно так его пишет сообщение. У свойств тип number, а не 5, потому что свойства объекта могут меняться. Когда вы добавите свойство в startingStats, оно появится и в Stats, и TypeScript сообщит о каждом объекте типа Stats, у которого его ещё нет.
Вы знаете typeof из JavaScript: typeof gold === "number". Тот typeof работает, пока выполняется программа, и даёт текст вроде "number" или "object". В типе (после type Stats = или после двоеточия) typeof принадлежит TypeScript: он даёт тип переменной и, как любой тип, исчезает из программы. Вы уже использовали его в уроке про моделирование данных: (typeof classes)[number].
const startingStats = { strength: 5, agility: 5, magic: 5 };
type Stats = typeof startingStats;
const copy: Stats = { ...startingStats };
console.log(typeof copy);
console.log(copy);Программа выводит:
object
{ strength: 5, agility: 5, magic: 5 }Значения и типы живут в двух разных мирах. startingStats — значение (переменная), Stats — тип, а typeof переносит тип значения в мир типов. Саму startingStats нельзя пометить типом Stats, потому что тип берётся из неё.
Типичные ошибки:
- Пропущен
typeof,type Stats = startingStats;. Сообщение подсказывает, что писать:main.ts(2,14): error TS2749: 'startingStats' refers to a value, but is being used as a type here. Did you mean 'typeof startingStats'? - Наоборот:
typeof Hero, гдеHero— тип, даёт'Hero' only refers to a type, but is being used as a value here.У типа нет значения, поэтому братьtypeofне от чего. Используйте типHeroнапрямую.
Задание
- 1
Под
startingStatsсоздайте типtype Stats = typeof startingStats;. Используйте его вaddStatsдля обоих параметров и для возвращаемого значения вместо трёх длинных типов, а ещё пометьте имwarriorBonusиmageBonus. Программа выводит то же, что и раньше. - 2
Герои получают новую характеристику — удачу. Добавьте
luck: 1вstartingStatsи запустите программу. Теперь уStatsесть иluck, а TypeScript сообщает о трёх местах, где её пока нет, ошибками вроде этой:main.ts(6,7): error TS2741: Property 'luck' is missing in type '{ strength: number; agility: number; magic: number; }' but required in type '{ strength: number; agility: number; magic: number; luck: number; }'.Исправьте все три: дайте воину бонус к удаче
0, а магу —2, и вaddStatsскладывайте и удачу тоже. Программа выводит:{ strength: 9, agility: 6, magic: 5, luck: 1 } { strength: 5, agility: 6, magic: 10, luck: 3 }