I don't mean to throw shade on the whole book, but I don't think the section on errors takes things in the right direction.<p>A distinction should be made between errors and exceptions. In JavaScript and many languages, we conflate the two and use exception handling as logic flow control. In my experience, this can end up being a headache and encourage unnecessarily weird structuring of code.<p>Look at this example from the page on errors:<p>---<p>function getAccount() {<p><pre><code> let accountName = prompt("Enter an account name");
if (!Object.hasOwn(accounts, accountName)) {
throw new Error(`No such account: ${accountName}`);
}
return accountName;
</code></pre>
}<p>---<p>The possibility that a user will enter a account name that doesn't exist is not an exception, but we are treating it like one in this case. In order to handle this exception when getAccount is called, we have to wrap it or some higher level scope in a try-block and then regex-match the error message if we want to handle it differently from other errors.<p>You might be saying "it's just an example", but there's plenty of production code in the wild that is written this way. Maybe this could be improved by subclassing Error, but now you're having to manage a bunch of clutter and using object-oriented features as a way to reliably determine what kind of exception you're dealing with.<p>I find this pattern to be preferable:<p>---<p>const ACCOUNT_NOT_FOUND_ERROR_CODE = 1;<p>function getAccount() {<p><pre><code> let accountName = prompt("Enter an account name");
if (!Object.hasOwn(accounts, accountName)) {
return {
accountName: null,
error: {
code: ACCOUNT_NOT_FOUND_ERROR_CODE,
message: `No such account: ${accountName}`,
}
};
}
return { accountName, error: null };</code></pre>
}<p>---<p>Then we can call the function like this:<p>---<p>const { accountName, error } = getAccount();<p>if (error) {<p><pre><code> if (error.code === ACCOUNT_NOT_FOUND_ERROR_CODE) {
errorModalService.show(error.message);
}
</code></pre>
} else {<p><pre><code> // do something with the account name
</code></pre>
}<p>---<p>No doubt, you may still want to catch exceptions at a higher level scope, but at the nice thing here is that exceptions (almost) always represent actual unexpected conditions that aren't being handled properly while return values with error codes represent expected conditions and can be handled like any other logic in your code. It also reduces any ambiguity of <i>how</i> an error should be handled but without subclassing. An error can even contain more information than just a message if you want it to.<p>Also, if you really want to ignore an error for some reason, then you can just pretend that the error doesn't exist. No need to use a try-catch where the catch-block is a no-op.<p>I wish we'd encourage this sort of pattern, but maybe that's one of many pipe dreams of mine.