Singleton Pattern in TypeScript

This post is 4 years old. (Or older!) Code samples may not work, screenshots may be missing and links could be broken. Although some of the content may be relevant please take it with a pinch of salt.

Design patterns play an important part in software development and they do help us to design better software and write better code.

GoF

Back in 1994, a book was authored by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides that discusses 23 desgin patterns, titled Design Patterns: Elements of Resuable Object-Oriented Software. You may have heard of this book or the authors as Gang of Four (GoF).

The book and the outlined design patterns in them formtulate a solid base for today's software development and those patterns introduced and explained in 1994 are still applicable today and can be used in modern day applications.

Design Pattern types

Design patterns can be categorised into the following categories based on what and how they aim to achieve

  • Creational: deal with object creation
  • Structural: deal with relationships between objects (or other entities)
  • Behavioural: deal with communication patterns between objects
  • Concurrency: deal with multi-threaded paradigms

Singleton Pattern

The singleton pattern is a creational software design pattern. This particular pattern is ensuring that a class has only one instance (an instance is a unique copy of a class) and via the Singleton we are provided a global point to accessing the instance.

When to use the singleton pattern?

There are some scenarios when it's as useful as important to have only one instance of a class. Examples would include situations when we are working with a shared resource - having a single entry point to a shared resource means one instance of a class, which also means that the application code is less prone to bugs.

TypeScript implementation

The singleton pattern could be implemented in any programming language, including vanilla JavaScript as well but I thought it'd be interesting to see how a TypeScript implementation would look like.

It goes without saying that should you want to get the JavaScript implementation, just transpile the TypeScript code to JavaScript, either to ES2015 or to ES5.

Singleton example

Let's take a look at a practical example of having a Singleton in TypeScript. Remember that the purpose of this pattern is that we can maintain a single entry point to a shared resource - in other words, we want to only allow one instantiation of a given class.

Let's imagine that we want to have a class that keeps track of temperature. In this system we want to have one entry point from which we can alter the temperature. This is how the Singleton class would look like:

class Singleton {
private static instance: Singleton;
private _temperature: number;
private constructor() {}
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
Singleton.instance._temperature = 0;
}
return Singleton.instance;
}
get temperature(): number {
return this._temperature;
}
set temperature(score) {
this._temperature = score;
}
increaseTemperature(): number {
return (this._temperature += 1);
}
decreaseTemperature(): number {
return (this._temperature -= 1);
}
}

A few things to note here:

  • the constructor is using the private access modifier - this will mean that we won't be able to instansiate the class using the new keyword.
  • We first check if we have an instance of the class, if we don't have one, we'll instansiate it and we return the instance itself.

Let's see what happens when we try to instansiate this class using the new keyword:

const myInstance = new Singleton(); // Constructor of class 'Singleton' is private and only accessible within the class declaration.

We do in fact get an error. So the only way to instansiate it is by calling the getInstance() method:

const myInstance = Singleton.getInstance();
console.log(myInstance.temperature); // 0

And as per the example above we can also access the temperature property. Let's now set the temperature value and increase/decrase it a few times:

console.log((myInstance.temperature = 25)); // 25
console.log(myInstance.increaseTemperature()); // 26
console.log(myInstance.increaseTemperature()); // 27
console.log(myInstance.decreaseTemperature()); // 26

Think about this for a moment. If at this point we'd be able to create a new instance of our class, we would be able to overwrite the temperature readings.

The only way we can "create" an instance is by calling the getInstance() method but that will realise that we already have an instance so it will return that existing one for us. And this is why we can access the temperature property and still get the last set value:

const myInstance2 = Singleton.getInstance();
console.log(myInstance2.temperature); // 26

And in fact if we compare the two instances we'll get true as they are exactly the same:

console.log(myInstance === myInstance2); // true