Types from Values
Usually, you write a type first and then values that fit it. Sometimes it's the other way round: the value comes first, and the type should follow it. Every new hero starts with the same stats, written once in an object:
const startingStats = { strength: 5, agility: 5, magic: 5 };Functions that work with stats need a type for them. You could write { strength: number; agility: number; magic: number } by hand, but then you'd have to change it every time you change the object. TypeScript can take the type from the value instead:
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 is the type that TypeScript inferred for startingStats, { strength: number; agility: number; magic: number; }, and that's how the message writes it. The properties are number, not 5, because the properties of an object can change. When you add a property to startingStats, Stats gets it too, and TypeScript reports every object of type Stats that doesn't have it yet.
You know typeof from JavaScript: typeof gold === "number". That typeof works while the program runs, and it gives a text such as "number" or "object". In a type (after type Stats =, or after a colon), typeof belongs to TypeScript: it gives the type of a variable, and like every type, it disappears from the program. You've used it before, in the lesson on modeling data: (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);This prints:
object
{ strength: 5, agility: 5, magic: 5 }Values and types live in two separate worlds. startingStats is a value (a variable), Stats is a type, and typeof brings the type of a value into the world of types. You can't annotate startingStats itself with Stats, because the type comes from it.
Typical mistakes:
- Leaving out
typeof,type Stats = startingStats;. The message tells you what to write:main.ts(2,14): error TS2749: 'startingStats' refers to a value, but is being used as a type here. Did you mean 'typeof startingStats'? - The other way round:
typeof Hero, whereHerois a type, gives'Hero' only refers to a type, but is being used as a value here.A type has no value, so there's nothing to taketypeofof. Use the typeHerodirectly.
Instructions
Under startingStats, create the type type Stats = typeof startingStats;. Use it in addStats for both parameters and the return type, instead of the three long types, and annotate warriorBonus and mageBonus with it too. The program prints the same as before.
Heroes get a new stat, luck. Add luck: 1 to startingStats and run the program. Stats now has luck too, and TypeScript reports three places that don't have it yet, with errors like this one:
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; }'.Fix all three: give the warrior a luck bonus of 0 and the mage 2, and in addStats, add up the luck too. The program prints:
{ strength: 9, agility: 6, magic: 5, luck: 1 }
{ strength: 5, agility: 6, magic: 10, luck: 3 }Start programming for free
5/7