Topic 2.4
switch Statements
In one line
switch compares one value against a list of case values with === and jumps to the first match; execution then falls through into the next cases until a break, and default handles no match. For pure value-to-value mapping, an object or Map is often better.
Think of it like this
A lift (elevator). You press one floor button. The lift goes straight to that floor, not to every floor asking "is it this one?". If the doors didn't close at your floor (no break), you'd keep going up through the next floors too. And if you press a floor that doesn't exist, you end up at the ground floor (the default).
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- switch
- A statement that compares one value against several
casevalues and jumps to the matching one. - case
- A label inside a
switchwith a value to compare against; matching is done with===. - Discriminant
- The value in
switch (...)that every case is compared to. It's evaluated once. - Fall-through
- Running on from one case into the next because there was no
break. - break
- A statement that leaves the
switch(or a loop) immediately. - default
- The part of a
switchthat runs when no case matched. - Lookup table
- An object or
Mapthat maps each input value to its result, used instead of aswitch.
Step by step
01The basic shape
One value, several cases, a break after each, and a default for anything else.
const light = "amber";
switch (light) {
case "green":
console.log("Go");
break;
case "amber":
console.log("Get ready to stop");
break;
case "red":
console.log("Stop");
break;
default:
console.log("Signal broken: stop and look");
}02How the jump works
The engine evaluates light once, then compares it with each case value using === until one matches. It jumps there and runs everything below until a break.
03Group cases on purpose
Stack case labels with nothing between them to give several values the same code. This is the useful side of fall-through.
switch (day) {
case "Sat":
case "Sun":
console.log("Weekend");
break;
default:
console.log("Weekday");
}04Give cases their own block when they declare variables
All cases share one scope. Braces after the case label give each case its own, so names can't clash.
switch (shape) {
case "square": {
const area = side * side;
console.log(area);
break;
}
case "circle": {
const area = Math.PI * r * r;
console.log(area);
break;
}
}05Replace value-only switches with a table
When each case only maps a value, a Map (or an object) does the job in a few lines, and adding a new size means adding data, not code.
const prices = new Map([
["small", 50],
["medium", 80],
["large", 120],
]);
console.log(prices.get("medium") ?? 0); // 80Try it yourself
- 1
Remove a break on purpose
In the first example, delete the
break;under"Fri". Predict what Friday's plan becomes, then choose Fri in the menu. (Work and study: it fell through into the next case.) - 2
Fix the type mismatch
In the second example, change
switch (input)toswitch (Number(input))and predict the output. - 3
Add an action
In the third example, add
half: (n) => n / 2toactionsand setcommandto"half". You added a feature with data, not a newcase.
Code & diagrams
Pick another day in the menu. Stacked case labels share one block of code.
Preview
Console
Nothing logged yet.Expected console output
Rest and play | Work, then movie night | Work and study | Unknown dayWith no `break`, case 1 runs on through 2 and 3 until the `break`. And `switch` uses `===`, so the string `"2"` never matches the number `2`.
Console
(nothing was printed)Expected console output
bronze
silver
gold
no match: '2' !== 2`prices["constructor"]` finds a function every object inherits. `Object.hasOwn` (ES2022) or a `Map` (ES2015) only accept keys you added yourself.
Console
(nothing was printed)Expected console output
80
0
function
unknown size
0
49Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Declare the same variable in two cases
Write a switch where case 1: contains let msg = "one"; and case 2: contains let msg = "two";, without braces.
Break #2
Forget a break
In the first example, delete the break; after plan = "Rest and play"; and choose Sat.
Myth vs fact
Myth
switch compares with ==, so "2" matches 2.
Fact
It uses strict equality (===). Convert the value first if it comes from text.
Myth
Only the matching case runs.
Fact
Execution starts at the matching case and keeps going through the following cases until a break, return or the end.
Myth
switch is always faster than if/else.
Fact
Engines may turn a dense integer or string switch into a jump table, but for a handful of cases the difference is negligible. Choose by readability.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
The spec evaluates case expressions lazily: each
caseexpression is only evaluated when the matching reaches it. Case values can be any expression (case LIMIT * 2:), not only literals, but non-constant cases prevent engines from building jump tables. - ▸
In TypeScript, a
switchover a union type with adefaultthat assigns the value tonevergives exhaustiveness checking: adding a new variant makes the compiler show you every switch that doesn't handle it. That's a strong reason to keepswitchfor state machines and reducers (for example Redux reducers switch onaction.type). - ▸
Mapbeats a plain object as a lookup table when keys aren't strings, when entries change at runtime, or when keys come from users (no prototype keys like__proto__orconstructor).Object.create(null)gives a prototype-less object if you prefer object syntax. - ▸
A TC39 proposal for pattern matching (
match) aims to replace manyswitchuses with destructuring-aware cases, but it is not part of the language as of 2026; don't rely on it.
Remember this
- 1
A
switchstatement takes one expression in parentheses, the discriminant, evaluates it once, and compares the result with eachcasevalue from top to bottom using strict equality (===, Topic 1.6). Soswitch ("1")does not matchcase 1:: a string and a number are never===. When a case matches, execution jumps to thatcaselabel and runs the statements after it. - 2
Fall-through: a
caseis only a starting point, not a box. After the matching case's statements, execution continues straight into the next case's statements, and the next, until it meets abreak(which exits theswitch), areturn(which exits the whole function) or the end of theswitch. Forgettingbreakis the most commonswitchbug. Fall-through is useful on purpose when several values share one action: write the cases one after another with no code between them (case "Sat": case "Sun": ...). - 3
default:runs when nocasematched. It's optional, but including it (even just to log or throw on an unexpected value) catches bugs early. It's conventionally last; if you put it elsewhere, it's still only chosen when nothing else matched, but it can fall through into the cases below it. Aswitchwith no matching case and nodefaultdoes nothing at all. - 4
Scope trap: the whole body of a
switchis one block. Declaringlet xin two different cases is a SyntaxError ("already been declared"), and aletfrom one case is visible, but uninitialised, in the others. Wrap each case's code in its own braces,case 1: { let x = ...; break; }, whenever it declares variables. - 5
When to prefer objects or Maps: if every case only returns or assigns a value ("small" → 50, "medium" → 80), a lookup table is shorter, has no
breakto forget and can be changed as data:const prices = { small: 50, medium: 80 }; const p = prices[size] ?? 0;. A plain object inherits keys liketoStringandconstructor, so an unexpected input could match them; useObject.hasOwn(prices, size)(ES2022),Object.create(null), or a **Map** (ES2015), whosegetreturnsundefinedfor anything you didn't put in. You can even store functions in a lookup table to map values to actions. Keepswitchfor cases that run different statements, or need fall-through. - 6
A pattern you'll see is
switch (true)with conditions as cases:case score >= 90:. It works because each case expression is evaluated and compared withtrue, but anif/else ifchain says the same thing more plainly (Topic 2.3).
Explain it without notes
How does switch decide which case matches?
What is fall-through, and when is it useful?
When would you use an object or Map instead of a switch?
Why can declaring let inside a case cause an error, and how do you fix it?
Practice
Write a switch that turns a number 1 to 3 into gold, silver, bronze, and anything else into no medal. Print the result for 2 and 7.
Rewrite the medal mapping as a lookup with a fallback, so there is no switch at all.
Trade-offs
- ↔
switchreads well for a fixed set of actions and supports fall-through; lookup tables are better for data-like mappings and are easier to extend. - ↔
A
Mapis safest for arbitrary keys; a plain object is shorter to write and fine for fixed, known string keys checked withObject.hasOwn.
Done when you can
I can write a
switchwithbreakanddefault.I know
switchmatches with===.I can use fall-through on purpose to group cases, and spot accidental fall-through.
I can wrap a case in braces when it declares variables.
I can replace a value-mapping
switchwith an object orMap.