JavaScript design patterns #5. The Observer pattern with TypeScript

JavaScript

This entry is part 5 of 5 in the JavaScript design patterns

The Observer pattern is common among various native JavaScript features and many libraries. The above is just one of the reasons to look into it more. In this article, we learn the principles of the Observer pattern. We also add some TypeScript typings to it, to make it more bug-proof.

The core principle of the Observer pattern is defining a mechanism of subscription to notify various objects about events. The notified objects have to explicitly state that they are interested in the above events first. The above is a common situation in JavaScript. We sometimes have some libraries taking care of the above tasks. On the other hand, it might be beneficial to know how it works under the hood.

The subject holds a list of observers and calls every one of them in case it wants to communicate something.

There are a few things to consider when designing a list of observers. Let’s assume that we want it to have no duplicates. The second thing that we want to provide is a straightforward way to unsubscribe from the list of observers. A better candidate than an array to implement the above features is a Set.

The Set object lets you store unique values of any type, whether primitive values or object references

The observer with the update function

There are a few approaches that we can take. The first of them includes creating an Observer class.

Our Observer class contains the   method. Let’s now expect every Observer to implement it. With that knowledge, we can continue writing the Subject class.

Since now we expect every Observer to implement the  method, we call it when we want to notify our observers.

Hello world!
Hello world!

Thanks to using a Set, we don’t have to worry about duplicates when subscribing. Therefore, they will be omitted.

An important note is that a Set looks at object references when checking for duplicates. Even though we added two identical observers, they are two different objects.

Hello world!

Since above we subscribe using the same observer twice, it is added only once.

The thing left to implement is the  method.

The same thing applies to the delete function of a Set. Therefore, our   function needs to be provided with the same object in order to delete it.

Unsubscribing to prevent the lapsed listener issue

Remember always to unsubscribe if you don’t need the observer to listen anymore. Forgetting to do so prevents the observer from being garbage-collected. This is an issue that we call the lapsed listener problem. A solution to the above problem might be using the WeakSet instead of Set, but it doesn’t allow us to iterate its elements.

If you want to know more about the garbage collector check out those two articles:

The observer as a function

Another popular approach would be to use functions as observers. This time we don’t need the Observer class.

Hello world!

The above approach might ring a bell for you, and rightfully so. We can often see it when using native JavaScript features.

We can take advantage of the above functionality and use it with our Subject to add some default logic to our event observers.

Adding TypeScript to our Observer

Expecting the Observer to have the   method or to be a function is a rather bold assumption. It is a very fitting place to introduce TypeScript. First, let’s define the basics of our Observer:

Above, we demand that every observer implements the   method.  We don’t enforce the message type here, so we leave it as any.

We use the above interface in our Subject class.

The essential thing above is that the Subject accepts any Observer as long as it has the   method.

The above approach makes our Subject highly reusable and generic while making sure that the   function is there.

Class ‘MyObserver’ incorrectly implements interface ‘Observer’.
Property ‘update’ is missing in type ‘MyObserver’ but required in type ‘Observer’.

Summary

In this article, we’ve gone through a popular and useful design pattern. Implementing it from the ground up gave us a bit of an insight into how this mechanism could work in some of the native JavaScript features. Aside from using JavaScript, we’ve also implemented TypeScript. By doing so, we’ve taken a precaution to make sure that we’ve built our observers properly. Learning all of the above surely gives us an additional tool under our programming belt.

Series Navigation<< JavaScript design patterns #4. Decorators and their implementation in TypeScript
Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Nick
Nick
3 years ago

thanks