switch Statement
The switch statement compares a single value against many possible matches and runs the code for the first one that fits. When you find yourself writing a long ladder of if / else if that all test the same variable, a switch usually reads cleaner and signals intent more clearly. Its one famous trap is fall-through: forget a break and execution keeps running into the next case. Understanding that behavior — and using it deliberately — is the key to writing correct switches.
Syntax
A switch evaluates one expression, then jumps to the case whose value matches it. The default clause runs when no case matches. Each case body typically ends with break to exit the switch.
const role = "editor";
switch (role) {
case "admin":
console.log("Full access");
break;
case "editor":
console.log("Can edit content");
break;
case "viewer":
console.log("Read-only access");
break;
default:
console.log("Unknown role");
}
Output:
Can edit content
The matched case runs, break stops execution, and control resumes after the closing brace.
Strict comparison
switch matches cases using strict equality (===), not loose equality. There is no type coercion, so the value and the case label must share the same type as well as value.
const code = "1";
switch (code) {
case 1:
console.log("number one");
break;
case "1":
console.log("string one");
break;
default:
console.log("no match");
}
Output:
string one
The string "1" never matches the number 1 — exactly as "1" === 1 is false. Convert deliberately before the switch if you need a particular type.
Fall-through and break
If a case body has no break (or return), execution falls through into the next case, ignoring its label. This is the single most common switch bug — and also a useful feature when you want several labels to share one body.
const day = "Sat";
switch (day) {
case "Sat":
case "Sun":
console.log("Weekend");
break;
default:
console.log("Weekday");
}
Output:
Weekend
Here "Sat" matches an empty case body, falls through to "Sun", and runs the shared code. Stacking labels like this is the idiomatic way to group cases.
Warning: Accidental fall-through is hard to spot. If you intend to fall through to the next case, leave a comment like
// falls throughso reviewers (and linters such as ESLint’sno-fallthroughrule) know it was on purpose.
default placement and return
default does not have to be last, though placing it last reads most naturally. Inside a function, you can use return instead of break — returning exits the function entirely, so it also stops the switch.
function statusColor(status) {
switch (status) {
case "success":
return "green";
case "warning":
return "amber";
case "error":
return "red";
default:
return "gray";
}
}
console.log(statusColor("warning"));
Output:
amber
Block scope inside cases
Cases share one block scope, so declaring let or const with the same name in two cases throws a redeclaration error. Wrap a case body in braces to give it its own scope.
switch (action.type) {
case "add": {
const total = action.a + action.b;
console.log(total);
break;
}
case "sub": {
const total = action.a - action.b;
console.log(total);
break;
}
}
switch vs if-else vs lookup objects
All three handle multi-way branching, but each fits a different shape of problem. A switch excels at comparing one value to discrete constants. An if/else chain wins when each branch tests a different condition or a range. A lookup object is often the cleanest choice when you are simply mapping a key to a value.
| Approach | Best for | Comparison | Notes |
|---|---|---|---|
switch | One value vs many discrete constants | Strict (===) | Watch for fall-through |
if / else if | Different conditions or ranges per branch | Any expression | Most flexible |
| Lookup object | Mapping a key to a value | Property lookup | Concise, no branching logic |
// switch territory: ranges, so if/else is clearer here
function tier(score) {
if (score >= 90) return "gold";
else if (score >= 70) return "silver";
else return "bronze";
}
// Pure value mapping: a lookup object beats a switch
const ICONS = {
success: "✅",
warning: "⚠️",
error: "❌",
};
const icon = ICONS[status] ?? "❓";
Reach for switch when you have several fixed cases against a single value and possibly shared bodies. Reach for a lookup object when there is no real logic — just a value per key.
Best Practices
- End every case with
breakorreturnunless you deliberately want fall-through. - Mark intentional fall-through with a
// falls throughcomment so it is unmistakable. - Wrap case bodies in
{ }when declaringlet/constto avoid scope clashes. - Always include a
defaultto handle unexpected values gracefully. - Remember matching is strict (
===) — coerce types before the switch if needed. - Prefer a lookup object when you are only mapping keys to values with no branching logic.
- Use an
if/elsechain when branches test ranges or differing conditions rather than one value.