Better errors handling for ES/Typescript classes

Writing a custom catch decorator.

In general, errors handling in JavaScript is not a very complicated topic, but it is very useful. When different errors occur during your application’s life, break it and leave the user bewildered by what is happening. 😵

If you want to protect a part of your code from possible errors, you can just run and test a block of code for errors and, if they occur, handle them and notify user with some message.

All of this can be done using try/catch blocks or .catch method of Promise objects. So far so good. In this post, we’ll dive in to bringing theory to practice with error handling in JavaScript applications. Let’s get started.

This post assumes basic-decent background in JavaScript.

All next examples will be shown in case of Vue class components, as typical errors handling situations. But it can be used with any ES/Typescript classes!

Moving on. ⏬

Tip: Use Bit to encapsulate modules/components with all their dependencies and setup. Share them in Bit’s cloud and collaborate with your team.

You should “try” 🧗‍

Most tutorials regarding error handling in Vue components (usually errors are displayed as “toast” messages to the user) will show an implementation that looks something like this (login example):

Don’t know what is `@Component`? It’s a decorator, which allows to use Vue component as class (read more).

The above implementation is correct and looks great.

We make an API request and wait for a response (token)

If there are no errors → handle response

If an error is catched in loginUser method → go to catch block and handle error

This looks easy 😎.

But, what happens when your application is larger that a small tutorial and it contains a lot of API requests and other stuff which can throw errors?

Well, it will look like this:

As you can see, alot of try/catch blocks make for some pretty code ugly, imperative and repetitive code (the same errors handling using toast notifications).

It’s better to make your code more declarative, describing what the program must accomplish, rather than just describe how to accomplish it.

So, we should find a way to hide this try/catch logic and find a more elegant solution to handle errors in our Vue.js app. Let’s give it a go.

Vue instruments ⛏

If you open Vue’s documentation, you can find a global options property named errorHandler , to which a handler for uncaught errors can be assigned. This should be the perfect option for us, so let’s try:

Great! 😃 We removed the ugly try/catch blocks and moved handling to theglobal errorHandler method. Let’s start the app:

Chrome DevTools console

Hmmm… Error from mounted hook catched, but the one from created is not. The reason for this (Vue’s documentation doesn’t tell anything about it) is that the errorHandler doesn’t catch errors in asynchronous methods (and native event handlers). Internally, Vue runs our created hook as synchronous, not waiting for completion of an asynchronous operation, when an error can actually be thrown.

Moving on. ⏬

Let’s catch 🎣

During research, I stumbled upon some Java code and saw interesting error handling, natively supported in Java:

Here throws IOException tells the compiler that some exception can be thrown in this method and should be handled. It would be great to implement something similar in our case.

However, JavaScript doesn’t have a native mechanism which allows us to implement such a functionality. True… but it does have decorators! 😈

A Decorator is a programming pattern, and below you can see one of it’s implementations:

For those who hear about it for the first time (read more), function log in the example above is a decorator. It takes getData function as an argument and extends the behavior of the latter function with logging functionality.

Decorator is a structural design pattern that lets you attach new behaviors to objects by placing them inside wrapper objects that contain these behaviors.

But JavaScript has it’s own decorators implementation — a function that is invoked with a prefixed @ symbol, and immediately followed by a class , parameter, method or property:

Here, @Catch() annotation is our decorator. Let’ implement it. Referencing the ES2016 specification, a decorator is an expression which returns function and can take as arguments:

target — class, where this decorator is actually used;

name — method/property name to which this decorator is applied;

property descriptor—specific object that describes the property’s attributes.

Base decorator:

First of all, we save the descriptor value to originalMethod . Then rewrite the original value with our version of target function, where we run original method with arguments from wrapper function.

We use .apply to link the method with an original context( this ). Calling original method wrapped with try/catch block, and, for now, just show messages on any error. After that, return descriptor back.

Note: await method works for both sync and async functions.

Using our custom decorator in a component:

Let’s start the app:

It works! 🎉 Now synchronous (from mounted hook) and asynchronous (from created hook) errors are properly handled and all this works with only one annotation.

You can also use it for regular Promise without async/await , it will work too:

For now, our decorator has a hardcoded error handler, and this solution is not very flexible. So, let’s create some store where we can register custom handler, in order to give the decorator the possibility to get this handler:

Here we created a catchDecoratorStore object with a setHandler method which set the handler to handler property. Then in catch block we check if handler exists, if so — run it, otherwise — show default console message.

Now we can use it very similar to how we used Vue global error handler:

Almost finished. It’s now a fully working implementation and you can use a decorator for any method, event handler and lifecycle hook.

But, what if some method needs a separate error handling logic?. In this case we should add an additional functionality to our decorator — the ability to accept arguments, in order to use it like this: @Catch(handler) .

The updated version:

Here the Catch function takes a handler as an argument and returns another function which is actually a decorator. The next change made here (line 11) is checking if the handler is passed to the decorator, and if so, we call it with an error object and the context (current object, where target method is located).

Our examples context is a Vue component. If the handler didn’t pass, we’ll call a global registered handler and console a message in other case.

With these last changes you can write components like this:

That’s it, you can now use global handler or, for special cases, local handlers. It also works with any ES6/Typescript classes.

You can find all the code from this article here, clone the repository and play with the source code.

🧐 I also wrote a catch-decorator library with the same concepts as in this article, but with some extended functionality, which you can find on Github and install from NPM:

npm install catch-decorator

Conclusion

Errors handling can be done in many ways, and you should find the best solution for your tasks and context. When a project is not very big, then moving errors handling to a separate makeRequest function for API response errors will be enough. But, when you use class components and want to create a unified way to handle errors in your app, decorators can be helpful.

I hope this post was useful 🎓. If you have any thoughts or questions, please feel free to respond and comment below! I will be glad to answer 🙂. Thanks.