Contents

Chapter 3

Values, and the Types They Insist On

let, const, and why TypeScript won't guess quietly.

Last chapter you gave a value a name with let, and TypeScript quietly decided what kind of value it was allowed to hold from then on. That was one line, dropped in early because you needed it to see a type error happen. Here’s the proper version.

let and const

let score = 7;
score = 9;

let declares a variable that can change. Make it, then replace what’s inside it whenever you like, the second line doesn’t repeat the type or the word let, that was only needed once, when the variable was created.

Some values shouldn’t change. A tax rate, a maximum, the number of cards in a deck. Say so with const instead:

const taxRate = 0.2;
taxRate = 0.25;
npx tsc rate.ts
rate.ts(2,1): error TS2588: Cannot assign to 'taxRate' because it is a constant.

const is a promise you make to TypeScript and it enforces on your behalf. It costs nothing to write and it turns “I’m fairly sure this doesn’t change” into something checked. Default to const. Reach for let only once you know a value actually needs to move.

Writing the type yourself

Last chapter, TypeScript worked out that score was a number from the 7 you gave it. You can also say so directly:

let score: number = 7;

A colon, then the type, sitting between the name and the value. For a line like this one, writing : number doesn’t teach TypeScript anything it couldn’t already see for itself, the value tells it everything. You’ll write annotations like this constantly once you’re passing values into functions in chapter 9, but for a plain variable with a value right there on the same line, most TypeScript code leaves it out and lets the value speak for itself. This book will do the same from here on, letting inference do the work unless there’s a specific reason to spell a type out.

There is one moment an annotation earns its keep today: a variable with nothing in it yet.

The three types you’ll use constantly

TypeHoldsExample
numberAny number, whole or notlet score = 7;
stringTextlet name = "Ada";
booleantrue or false, and nothing elselet ready = true;

Three, not four. If you’ve seen another language split numbers into an integer type and a decimal type, TypeScript doesn’t, 7 and 2.5 are both just number. That simplicity has a cost, chapter 6 is where you’ll meet it.

Exercise 1 · Lie to it on purpose, this time with an annotation

Chapter 2 caused a type error by reassigning a variable. This time, cause one on the very line a variable is created, with an explicit annotation telling on you:

let price: number = "free";
let ready: boolean = "yes";

Compile it and read both messages. They’re the same shape as chapter 2’s, because they’re the same underlying check, TypeScript comparing a value against a type it already knows, it just usually learns that type from the value rather than being told outright like this.

Giving TypeScript enough to work with

Chapter 2’s whole lesson depended on TypeScript knowing what score was supposed to be. That knowledge has to come from somewhere, and if you don’t give it anything, it stops checking rather than guessing:

let x;
x = 7;
x = "seven";
console.log(x);
npx tsc loose.ts && node loose.js
seven

tsc printed nothing, which means no error, and the program ran fine and reassigned a plain number to a plain string along the way, with TypeScript’s full blessing. Chapter 2 would have stopped you doing exactly this.

Give it something and the checking comes back, even without a value yet:

let score: number;
console.log(score);
npx tsc unset.ts
unset.ts(2,13): error TS2454: Variable 'score' is used before being assigned.

An annotation alone is enough for TypeScript to hold you to it. It won’t let you read score until something has actually gone into it, not even to print whatever garbage happened to be sitting there. That refusal is the whole point, there’s no “probably a number”, there’s an error, on the spot, naming the exact variable.

What you’re allowed to call things

Names can use letters, digits, underscores and $, and can’t start with a digit. score, total_price and player2 are fine, 2ndPlace isn’t. Case matters, score and Score are two different variables, which is a fine way to confuse yourself at midnight.

Beyond the rules TypeScript enforces, use names that say what the thing is. totalPrice beats tp, and it beats x by a mile. You’re writing for the version of you that comes back in a month having forgotten all of this.

Check yourself

1. What is the real difference between let and const?

Not quite. Both work with every type. The difference has nothing to do with what kind of value is inside.

Yes. That is the entire distinction. Reach for const by default and switch to let only once a value genuinely needs to change.

Not quite. TypeScript checks both equally. const is about reassignment, not about type checking.

2. let x; with no value and no annotation. What type does x get?

Not quite. It compiles fine. That is exactly the surprising part.

Yes. With nothing to reason from, TypeScript opts out rather than guessing. Chapter 30 covers where else any turns up.

Not quite. TypeScript never assumes a specific type without evidence. With no value and no annotation there is no evidence.

3. let score: number; then console.log(score); before score is ever set. What happens?

Not quite. That is closer to what plain JavaScript would do. TypeScript catches this one before it runs.

Yes. The annotation gave TypeScript enough to hold you to, and reading an unset variable breaks that promise.

Not quite. TypeScript has no default values. An unset number is not zero, it is nothing, and reading it is the error.

Project

A receipt

Roughly 30 minutes

Write a program that prints a receipt for a single item. It should hold, in properly named variables:

  • the item’s name, as a string
  • how many were bought, as a number
  • the price of one, as a number
  • a tax rate, as a const, since it isn’t going to change while the program runs, so say so

Then work out the subtotal, the tax, and the total, and print something like:

RECEIPT
Mechanical keyboard
2 x 49.5
Subtotal: 99
Tax: 19.8
Total: 118.8

Rules: every variable gets a value on the line that creates it, and every name says what it holds. No x, no t2, and steer clear of name for the reason above.

Stretch: add a second item with its own name, quantity, and price, and make the total cover both. You’ll notice you’re copying three variable declarations and changing the names. Hold that thought, it’s the itch chapters 9 and 12 scratch.