Design Patterns in TypeScript - Singleton
Tags: Typescript, Singleton Pattern, Design Patterns, Programming, Singleton

Are we done?

Uhm… Not yet.

TypeScript is a superset of JavaScript. It extends JavaScript by adding types to the language. So basically we’re running JS code at the end of the day. The tsconfig.json and eslint or the deprecated tslint could be confusing sometimes, so if the compiler doesn’t fail at compiling or at least warn us about the class initialization, we can get compiled JavaScript code:

The compiled JS code can create an instance

And guess what… You can run this “without” any issue! What happened?

In ES6 classes the members don’t have access level. Everything is just public. ES2020 introduced private access level via # character for properties and methods as well, but because of the browsers, we won’t see ES2020 soon on every website.

So yes, basically the code is useless, it can’t prevent anyone to create a new instance of the cache.

What can we do then?

Use the benefits of modules and module caching! When you require() anything, then the V8 engine will cache the required object. If you use require again, the object will come from the require cache . With this feature, we can easily create a singleton instance of our example cache!

Try it!

Use Node.JS module system caching mechanism to create a singleton

You just have to import the CacheService that is actually only an instance of the Cache class, and you can use it.

Because of the require built-in cache mechanism, we don’t have to worry about race conditions, the instance creation will occur only once.

One more thing

Cool, I have finally a global cache which is blazingly fast! How can I use it?

Remember, only the instance is exported, the class itself is not! Thanks to this there is a new issue: when using CacheService there aren’t types.

Oh crap! But I want to use my fancy strictly typed methods!

It’s easier than you think. Create an interface for it:

Use interface to get the typings for the hidden class

The interface can be exported because they are vanishing after compiling, but they can help us how to use the singleton class.

The final class with strict types can be found on GitHub.

What did we achieve with this?

The singleton design pattern solves problems like:

How can it be ensured that a class has only one instance?

Just export an instance, not the class itself.

How can the sole instance of a class be accessed easily?

Import the instance.

How can a class control its instantiation?

Actually, we can’t, because the constructor can’t be private. Okay… It can be via usual JS hacking. Don’t do this, please.

How can the number of instances of a class be restricted?

The import cache takes care of the number of instances.

How can a global variable be accessed?

Import the instance.

When to use this design pattern?

When you have a shared resource like a database/cache/a piece of hardware (robotic arms!). Of course, you can use your Logger as a singleton, but in most cases a static class fairly enough.

The connection to the database in a singleton class can avoid too many connections. In a NodeJS environment, your app is running constantly, so you don’t have to reconnect to the DB every time (good old days with PHP). You can use multiple connections like a connection pool, but you can still create the connection pool in the singleton.

The choice is yours, but don’t forget! Don’t overuse this pattern. As Abraham Maslow said:

“I suppose it is tempting, if the only tool you have is a hammer, to treat everything as if it were a nail.”

Problems with singleton

This pattern is generally considered as an “Anti-pattern” as I mentioned before, for the following reasons:

You cannot inherit from a Singleton Class. To add new functionality you cannot descend a new class to contain that functionality, breaking Separation of Concern. You do not have control over creation. When you access the instance of the class you cannot tell if it is an existing instance or a new instance that is returned. You cannot do Dependency Injection. As there is only a single instance of the class you cannot inject a dependency into the class. If done via a property you are changing the dependency for all references to that instance. As you have no control over creation, you cannot use a “Clean” instance of the object for each test. Without Dependency Injection, you cannot use Mock Objects in your tests.

Before using this pattern consider if you need any of these. If the answer is yes, then you have to rethink the solution.

Conclusion

The singleton pattern is not evil or good. It’s just a way to solve the problems in your code. Use it wisely and carefully ;)