Several years ago, the entire booking and check-in system for a major airline in the United States ceased to function for more than an hour during the morning rush on a weekday. This resulted in flight delays for the entire day all across the country. In the end, the cause was found to be an unhanded error that resulted in a flight lookup service to become completely unresponsive.

Handling errors in TypeScript and JavaScript is one of the fundamental things a developer should be experienced in. It’s as important as the rest of the code, and should never be overlooked or underestimated. This is a guide to help newer developers understand how to handle errors, throwing errors, and using try/catch/finally according to industry standards, and also how not to handle them.

Best Practice – Getting Type Information When Catching Errors

Here is a method of getting type information when catching an error:

As shown above, attempting to find the type of error you are handling can get quite tedious. In most cases, you will know what you kinds of error situations you can recover from. The rest should be re-thrown and never swallowed (more on this later). Here is a much simplified and more typical version:

Best Practice – Always Use Error Objects, Never Scalars

When throwing or returning errors, always use instances of the Error object or objects derived from it. Never throw scalars (e.g. number, naked strings, etc). This ensure stack traces are collected and error typing can be used when handling the error.

Here is an example of returning an Error in code utilizing callbacks:

Here is an example of throwing an error in an async function:

Notice how much less code is needed and how it much simpler it appears. This is a common theme when using async/await. I cannot recommend it enough. Back to errors…

Best Practice – Free Resources in Finally

The finally clause should be used to free any resources.

Best Practice – Be Specific with Errors

Often you will want to throw or return errors that are more specific than the generic Error object. That is easy to do by extending the standard error object.

The goal with having a more specific error is to maximize the chance that the error can be handled locally and allow code execution to continue. Here is how you catch the specific errors.

Errors within the “if” conditions must be listed from most specific to least specific. After all, anĀ InvalidArgumentError is an instance of an Error too.

Best Practice – Never Swallow Exceptions

As far as the browser is concerned as long as the Error is caught, it will happily continue executing your code. In fact, doing something with the Error in the catch block is entirely optional. Swallowing an exception refers to the act of catching an Error and then doing nothing to fix the problem. Here is an example of swallowing an Error.

While valid code, it is very bad practice. At a minimum, the Error should be re-thrown.

This is valid and the compiler doesn’t complain, but please don’t ever do this unless you have an extremely well documented reason. While the exception is caught, we do nothing to fix the arising issue and any kind of useful information we could extract from the caught error thrown on the floor and is lost.

Another common and not very helpful practice is to log the Error to the console and continue:

Errors can also be swallowed by abruptly returning in a finally block:

First the error is thrown. Then the finally block is executed and the code abruptly returns resulting in all error information being lost. The error is again swallowed.

Errors can also be swallowed by shadowing them:

Best Practice – Never Use throw as a GoTo

Sometimes, someone will think they are clever by using the try/catch mechanism as way to control code flow when an error condition doesn’t really exist.

The end goal of this code example is to skip “some code 2”. This is ineffective and slow due to try/catch being designed for error conditions, not regular program logic flow. JavaScript/TypeScript offers more than enough flow control logic to do just about anything, so simulating a goto statement is not the best idea.

Best Practice – Never Catch for the Purpose of Logging and Then Re-throwing

When trying to figure out why your application is crashing, do not both log the exception and then immediate re-throw it:

Doing both is redundant and will result in multiple log messages that will clog your logs with the amount of text.