Contents

Chapter 7

Deciding Things: if, else, and Truthiness

Comparison, &&, ||, and JavaScript's truthy/falsy list.

Every program you’ve written runs every line, top to bottom, whatever happens. That’s fine for a receipt and useless for anything that has to react to what it’s given.

if

const age = 15;

if (age < 18) {
  console.log("Too young.");
}
Too young.

The condition goes in parentheses, and the block in braces runs only when it’s true. Nothing else about the program changes.

else covers the other case, and else if chains more:

const age = 15;

if (age < 13) {
  console.log("Child");
} else if (age < 18) {
  console.log("Teenager");
} else {
  console.log("Adult");
}
Teenager

Read that top to bottom, because that’s how it runs. The first condition that’s true wins and the rest are skipped, which is why age < 18 doesn’t need to also say “and at least 13.” By the time you reach it, age < 13 has already been ruled out.

Comparing things

OperatorAsks
===are these the same?
!==are these different?
> <greater, less
>= <=greater or equal, less or equal
let a = 5;
let b = 2;
console.log(a === b, a !== b, a > b, a >= 5, b < 1);
false true true true false

Three equals signs, not one. Chapter 3 warned that = is an instruction rather than a question, and this is where that matters.

Combining conditions

&& means both, || means either, ! flips one:

const age = 25;
const isWeekend = true;

if (age >= 18 && isWeekend) {
  console.log("Adult weekend ticket");
}

if (age < 13 || age >= 65) {
  console.log("Discounted");
}
Adult weekend ticket

Note that isWeekend needs no comparison. It’s already a boolean, so isWeekend === true is just a longer way of writing the same thing.

Truthiness

Here’s where JavaScript’s history shows through, and where TypeScript stops protecting you.

An if doesn’t require a boolean. Give it anything at all and it will decide whether that thing counts as true. Most values count as true. This is the complete list of the ones that don’t:

Falsy
falsethe actual boolean
0the number zero
""the empty string
null
undefined
NaNchapter 5’s not-a-number

Six values. Everything else is truthy, and that list is worth memorising because the alternative is guessing.

console.log(Boolean(0));
console.log(Boolean("0"));
console.log(Boolean(""));
console.log(Boolean("false"));
false
true
false
true

Read those middle two again. The number 0 is falsy. The string "0" is truthy. And "false", the word, is truthy too, because it’s a non-empty string and that’s the only question being asked.

Exercise 1 · Ask the right question

Write a program that reads one line and reports whether the number in it is zero. Feed it 0, then 5, then a blank line.

Do it once with if (answer) and watch it get the first case wrong. Then do it with Number(answer) === 0 and watch it get all three right. The difference between “is there a value” and “what is the value” is the whole lesson.

Comparing decimals, at last

Chapter 6 left this hanging: 0.1 + 0.2 === 0.3 is false, and you now have the tools to do something about it. Don’t ask whether two decimals are equal, ask whether they’re close enough:

console.log(Math.abs(0.1 + 0.2 - 0.3) < 0.0000001);
true

Subtract one from the other, take the absolute value with Math.abs() so the order doesn’t matter, and check the gap is tiny. That’s the standard answer in every language that stores numbers this way.

Check yourself

1. const answer = "0"; if (answer) { ... } Does the block run?

Not quite. The number 0 is falsy. This is the string "0", which is a non-empty string, and every non-empty string is truthy.

Yes. Truthiness asks whether there is something there, not what it is worth. Since chapter 5 means all input arrives as strings, this is a real trap and not a curiosity.

Not quite. An if accepts any value at all and decides truthiness itself. That is exactly why the falsy list is worth memorising.

2. let score = 5; if (score = 10) { ... }. What happens?

Not quite. It compiles clean. Assignment hands back the value assigned, and 10 is a perfectly good thing for an if to look at.

Yes. The assignment happened, its result was 10, and 10 is truthy. The comparison you meant to write never happened at all.

Not quite. That would be === doing its job. A single = does not compare anything.

3. Why does if (text.length > 0 && text[0] === "A") stay safe on an empty string?

Not quite. It stops as soon as the answer is settled. If it evaluated both sides every time, the guard would be pointless.

Yes. False on the left settles the whole expression, so the second half is skipped entirely. Swap the two halves and the protection is gone.

Not quite. It gives you undefined rather than crashing, but the point stands: the guard is what makes the order correct.

Project

Ticket pricing with real rules

Roughly 40 minutes

Work out the price of a cinema ticket. Start with the inputs hardcoded in consts at the top, an age and whether it’s a weekend.

The rules, in the order a human would say them:

  • Base price is 12.
  • Under 13, or 65 and over, pays 8 instead.
  • Weekend adds 3 to whatever the price would otherwise be.
  • Under 5 is free, whatever day it is.

Print the age, the day, and the final price.

Age 8, weekend: 11

The whole exercise is the last rule. Three of those rules stack neatly and one overrides everything. If you write them in the order listed, a four-year-old on a Saturday ends up paying 3, because the surcharge was applied after you set the price to zero.

There’s more than one right fix. You can check the free case first and skip the rest, or apply the surcharge only when the price isn’t zero. Pick one deliberately, and write a comment saying why.

Test the boundaries, not the middle. Ages 4, 5, 12, 13, 64 and 65 are where the rules change, and where an accidental < instead of <= shows up. Check each one on both a weekday and a weekend.

Stretch: add a member discount of 2, applied last, that can’t take the price below zero. Notice you now have four rules interacting and the ordering question got harder rather than easier. That’s the itch chapter 9 scratches, giving each rule a name of its own.