You may be doing try/catch all wrong

What if I told you that you're doing try/catch in JavaScript all wrong? I'll discuss three things that you should be doing differently in your try/catch blocks.

You probably think I'm crazy when I say that you're doing your try/catch statements in JavaScript all wrong. But for some of you, it may be true.

Optional catch binding

Let's examine a simple example where we have some code that does something (Hmm… doesn't all code do something? 🤷). However, if there are any errors, we want to ignore them (i.e. fail safe).

You may have some code that looks like this laying around.

try {
  doSomething();
} catch (err) {
  // ignore error
}

Ok, so what's the problem? The problem is that if we're not going to do anything with the exception passed to the catch, we're still creating and assigning the variable err.

Fortunately, passing an exception to the catch block is optional. The syntax looks like this.

try {
  doSomething();
} catch {
  // ignore error
}

It's not the end of the world, but it's less code that we don't need to write and maintain. And, depending on your lint setting, an unused parameter may show as a warning.

This is what MDN refers to as an optional catch binding and is available for use in all modern browsers (sorry IE).

Don't assume it's an error

That's pretty good, but what if we wanted to do something with the error?

Let's look again at our code from above, but this time we want log any errors to the console if the error message starts with "LOG". (This is for demonstration purposes only. I don't suggesting you actually do this.)

try {
  doSomething();
} catch (err) {
  if (err.message.startsWith('LOG')) {
    console.log(err.message);
  }
}

There's still issues with this code. Do you know what the problem is? Well, you can't assume that the exception passed to the catch block is of type Error. There's nothing preventing doSomething from throwing a string, a number, or any other type of data. If doSomething throws a "7" (for example), our catch would fail with

TypeError: Cannot read properties of undefined (reading 'startsWith')

So what can we do?

If you need to interrogate the error, you can use the instanceof operator. In other words, make sure it's an Error before you treat it as such.

try {
  doSomething();
} catch (err) {
  if (err instanceof Error && err.message.startsWith('LOG')) {
    console.log(err.message);
  }
}

If you are using TypeScript 4.4 or later, there is a new flag that I highly recommend that changes the default type of catch variables from any to unknown. See Microsoft's TypeScript 4.4 announcement for more information.

Naming things is hard

And lastly, as I pointed out above, you can't assume what you are catching is an Error. So why are you calling it err or error? Let's call it what it is, an exception.

try {
  doSomething();
} catch (exception) {
  if (exception instanceof Error && exception.message.startsWith('LOG')) {
    console.log(exception.message);
  }
}

Or, if you prefer, you can use a temporary variable to be even more descriptive. And some people say there's no such thing as self-documenting code.

try {
  doSomething();
} catch (exception) {
  if (exception instanceof Error) {
    const error = exception;

    if (error.message.startsWith('LOG')) {
      console.log(error.message);
    }
  }
}