I prefer early return pattern.<p>Which would look like this:<p><pre><code> const user = {
firstName: "Seán",
lastName: "Barry",
email: "my.address@email.com",
number: "00447123456789",
};
if (!user) {
throw new Error("User must be defined.");
}
if (!user.firstName) {
throw new Error("User's first name must be defined");
}
if (typeof user.firstName !== "string") {
throw new Error("User's first name must be a string");
}
return user;</code></pre>
A good example of: just because you can, it doesn’t mean you should.
Yes, the if/else sequence is a bit more unstructured in terms of code layout, but chances the next developer browsing the code will instantly know what it does will be much higher.
I think the post doesn't give a fair comparison as in the if/else case there's no need to use "else" blocks if you're throwing an error or returning. In this case I think simple "if" statements are cleaner and certainly more "normal".<p>E.g.<p><pre><code> if (!user) {
throw new Error("User must be defined.");
}
if (!user.firstName) {
throw new Error("User's first name must be defined");
}
return user;</code></pre>
Is this a real thing? It looks incredibly hacky to me. What happens when multiple cases are true, are they all handled? In what order? What happens if one of them returns? Etc.
Hopefully we will get real pattern-matching at some point (<a href="https://github.com/tc39/proposal-pattern-matching" rel="nofollow">https://github.com/tc39/proposal-pattern-matching</a>), but I kinda sorta like this!
Guaranteed to always occur:<p><pre><code> ...
case !isValidPhoneNumber(user.email):
...
</code></pre>
Tho see [1] and [2]<p>[1]: <a href="https://github.com/kdeldycke/awesome-falsehood#emails" rel="nofollow">https://github.com/kdeldycke/awesome-falsehood#emails</a><p>[2]: <a href="https://github.com/kdeldycke/awesome-falsehood#phone-numbers" rel="nofollow">https://github.com/kdeldycke/awesome-falsehood#phone-numbers</a>
Validation code as shown tends to be repetitive and imperfectly implemented. I have found that transitioning to using AJV and JSON Schema is far more sustainable, especially on an API surface. One describes the data and depends on consistent and vetted logic for validating the described types rather than repetitively describing how to validate them.<p>Validations that happened at an application level must still be written but those tend to be specific to the application logic or system state. An example of logic related validation is contingently valid argument values where the compatibility of one value being used with another must be tested. An example of state related validation is a constraint that a given value must exist in a dynamic table.
This style gets the label 'poor-mans-pattern-matching' from me. If pattern matching would not be in my daily vocabulary, as it's also not available in JS, I'd consider it a misuse of switch/case and this post also makes an odd example for its usefulness.<p>The example I would pick is the following: Consider you need to switch depending on a version (of a specification in my case), but this version isn't represented as an enum in the codebase, but as a number instead. So our team had something like this in the codebase (early return):<p><pre><code> function foobar(version: number): string {
if (version === 3.1 || version === 3) {
return 'result_3';
}
if (version < 2 && version >= 1) {
return 'result_1';
}
if (version >= 2) {
return 'result_2';
}
throw new Error(`Cannot interpret version '${version}'`);
}
</code></pre>
I read it as "people don't care about branching order that much, so how can I make my wish for better readability more clear?".... my end goal then was to bring it into this state (a distinct enum as discrete value of the version):<p><pre><code> enum Version {
_1_1 = 1.1,
_2 = 2,
_3 = 3,
_3_1 = 3.1,
};
function foobar(version: Version): string {
switch (version) {
case Version._3_1:
case Version._3:
return 'result_3';
case Version._2:
return 'result_2';
case Version._1_1:
return 'result_1';
default:
(function (val: never): never {
throw new Error(`Exhaustiveness reached: ${val}`);
})(version);
}
}
</code></pre>
...and my interim solution that made it into the PR in time turned out to be something like this (switch true):<p><pre><code> function foobar(version: number): string {
switch (true) {
case version >= 3:
return 'result_3';
case version >= 2:
return 'result_2';
case version >= 1:
return 'result_1';
default:
throw new Error(`Cannot interpret version '${version}'`);
}
}
</code></pre>
My PR was flagged by the team for misuse of the switch statement, we had some discussion and I changed it back to the simple if/else branching from above.
Good lord this is awful. If somebody's paying you to solve problems with code, please just write clear code, rather than showing off. Somebody's going to have to make sense of it a year from now when requirements change, and you will be in a sense talking to that future programmer (maybe it's you) via code. You should be trying to tell them about the problem, rather than about yourself.
In addition to an already mentioned early return pattern, I also often recommend switching from switch to pattern matching via an object.<p>It has an additional benefit of extracting code into data structures or making them parametric<p><pre><code> function getArrow(direction) {
switch (direction) {
case "left":
return "<--"
case "righ":
return "-->"
default:
return "¯\\_(ツ)_/¯"
}
}
function getArrow(direction) {
const arrows = {
left: "<--",
right: "-->",
default: "¯\\_(ツ)_/¯",
}
let arrow = arrows[direction]
if (arrow) {
return arrow
} else {
return arrow.default
}
}</code></pre>
On a similar topic, I'm wondering how often people are using the "else" part of if/else these days. I haven't written "else" in years and I've become very fond of that "if only" pattern.
I think the beauty of the switch statement is that when you see one you know you're just concentrating on the value of one variable. I think the if else if is actually cleaner for the user example in the post.
That's good and dandy until one changes one case block to normal statements instead of a terminating one, forgets to add a "break;" and someone has a nightmare debugging session trying to figure what is going on.<p>Go did good by making case blocks break automatically and requiring the "fallthrough" keyword in one of those very rare cases you need it do.
The article is misleading, in implying that `switch(true)` is a special case: "The fundamental principle of the switch true pattern is that you can match against expressions as well as values."<p>It should be states as "The fundamental principle of the switch pattern in JavaScript is that you can match against expressions as well as values."<p>From <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...</a>:<p>A switch statement first evaluates its expression. It then looks for the first case clause whose expression evaluates to the same value as the result of the input expression
For many years I stopped using this after getting flack in pull requests.<p>I’ve recently added it back into my toolkit and am reminded how much I love it. Don’t over use it but there are some really gnarly if/else blocks that can be expressed beautifully with a switch and fall-thru.
Even accepting the dubious premise that "pretty text = maintainable code", he's juked his exampled by (i) littering the simple early-outs with unnecessary "else"s and (ii) stripping the "break"s from his switch.
I remember when I first discovered the concept of pattern matching, I tried to find a way to "hack" the switch statement in js to make it work like pattern matching.<p>The switch(true) format was the closest I got to it, which I personally don't like compared to a clean if/else or early return.<p>There's probably some performance differences between if/else and switch (I haven't checked) but it probably end up being what's your preference / what standard code style you want to enforce on your team.
Reminds me of a C++ pattern some guy I worked with years ago used to love to simplify complex if checks - using do...while(false) to create a scope you can easily break out of. e.g.<p><pre><code> bool success = false;
do {
if (!someCondition) break;
if (!otherCondition) break;
...
success = true;
} while(false);
if (!success) {
...
}
</code></pre>
I personally disliked it, plus it can lead to funky behavior under optimization.
The example shouldn’t be compared to esleif, that’s not how switches work.<p>Also, because it throws you can just use if, without a block. Or use if at the end of the line if your language supports that.<p>Way cleaner (less indentation), and less error prone. Switch statements only exist because of the underlying assembly/opcode.<p>It just as bad as goto, because it IS goto. The cases are goto-labels. It behaves like goto, and will simply generate the same JE/JNE jumps
It's interesting, but I can't decide if it's an anti-pattern or not. You're abusing a construct to achieve a slight improvement in brevity/readability, with the downside of JS's lack of block-scoping for case statements which means variables in one case can conflict with variables in other cases<p>All in all: I probably won't be using it
While readable and aesthetically pleasing, I find myself wondering about the performance implications of switch(true) versus a multi-branched if-else. Does V8 (and PHP) treat each construct differently when it comes to optimizations? We're not in C-land here, so jump tables are presumably not in play.
```
switch (true) {
case 1 + 1 === 2:
// This case evaluates to true so it will be executed
default:
// This will not be executed
}
```<p>This is wrong, Since there is no return or break default will also be executed.