The proposal “Optional catch binding” by Michael Ficarra is at stage 4 and therefore part of ECMAScript 2019. This blog post explains how it works.
The proposal allows you to do the following:
try {
···
} catch {
···
}
That is useful whenever you don’t need the binding (“parameter”) of the catch
clause:
try {
···
} catch (error) {
···
}
If you never use the variable error
, you may as well omit it, but JavaScript doesn’t let you do catch ()
. Furthermore, linters that check for unused variables complain in such cases.
There are two general reasons for omitting the catch
binding:
My recommendation is to avoid doing that:
If you can’t and don’t want to avoid it, I suggest encapsulating your code, e.g. inside a function, and to document it well.
Next, we’ll take a look at use cases for omitting catch
bindings and at risks and alternatives.
JSON.parse()
With JSON.parse()
, there is one predictable kind of exception – if the input is not legal JSON:
> JSON.parse('abc')
SyntaxError: Unexpected token a in JSON at position 0
That’s why it can make sense to use it like this:
let jsonData;
try {
jsonData = JSON.parse(str); // (A)
} catch {
jsonData = DEFAULT_DATA;
}
There is one problem with this approach: errors in line A that are not related to parsing will be silently ignored. For example, you may make a typo such as JSON.prase(str)
. Cases like this have bitten me a few times in the past. Therefore, I now prefer to conditionally re-throw the errors I catch:
let jsonData;
try {
jsonData = JSON.parse(str);
} catch (err) {
if (err instanceof SyntaxError) {
jsonData = DEFAULT_DATA;
} else {
throw err;
}
}
When accessing nested properties that may or may not exist, you can avoid checking for their existence if you simply access them and use a default if there is an exception:
function logId(person) {
let id = 'No ID';
try {
id = person.data.id;
} catch {}
console.log(id);
}
I prefer explicit checks. For example:
function logId(person) {
let id = 'No ID';
if (person && person.data && person.data.id) {
id = person.data.id;
}
console.log(id);
}
This code can be shortened if you consider that the &&
operator returns the first falsy operand or the last operand (if there is no falsy operand):
function logId(person) {
let id = (person && person.data && person.data.id) || 'No ID';
console.log(id);
}
However, this shorter version is also more obscure.
assert.throws()
Node.js has the API function assert.throws(func)
that checks whether an error is thrown inside func
. It could be implemented as follows.
function throws(func) {
try {
func();
} catch {
return; // everything OK
}
throw new Error('Function didn’t throw an exception!');
}
This function is an example of wrapping and documenting code that ignores caught exceptions.
The following code snippet demonstrates how to detect whether a given feature exists:
let supported;
try {
useTheFeature();
supported = true;
} catch {
supported = false;
}
If even logging doesn’t work then, as a last resort, you have no choice but to ignore exceptions (because further logging could make things worse).
function logError(err) {
try {
// Log or otherwise report the error
console.error(err);
} catch {} // there is nothing we can do
}
Again, we encapsulate and document the slightly unorthodox code.