Singleton Pattern in TypeScript
Older Article
This article was published 9 years ago. Some information may be outdated or no longer applicable.
Design patterns help us write better software. Full stop.
GoF
Back in 1994, Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides published Design Patterns: Elements of Reusable Object-Oriented Software. You might know the authors as the Gang of Four (GoF).
The 23 patterns they outlined still hold up. They form a solid base for modern software development, and they’re just as applicable now as they were then.
Design Pattern types
Design patterns break down into categories based on what 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 is a creational pattern. It guarantees a class has only one instance (a single unique copy) and provides a global access point to that instance.
When to use the singleton pattern?
Some situations demand exactly one instance. Working with a shared resource is the classic example. A single entry point means fewer bugs, because no competing instances can step on each other.
TypeScript implementation
You could implement the Singleton in any language, vanilla JavaScript included. But let’s see what it looks like in TypeScript.
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
Here’s a practical example. We want a class that tracks temperature. The system should have exactly one entry point for altering that temperature. Here’s the Singleton:
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 notice:
- The constructor uses the
privateaccess modifier, so you can’t instantiate the class withnew. getInstance()checks whether an instance exists. If not, it creates one. Either way, it returns the instance.
Try instantiating with new:
const myInstance = new Singleton(); // Constructor of class 'Singleton' is private and only accessible within the class declaration.
Error. The only way in is through getInstance():
const myInstance = Singleton.getInstance();
console.log(myInstance.temperature); // 0
We can access the temperature property. Let’s set it and bump it around:
console.log((myInstance.temperature = 25)); // 25
console.log(myInstance.increaseTemperature()); // 26
console.log(myInstance.increaseTemperature()); // 27
console.log(myInstance.decreaseTemperature()); // 26
Think about what would happen if we could create a second instance. We’d be able to overwrite the temperature readings.
But calling getInstance() again doesn’t create a new instance. It spots the existing one and returns it. The temperature value carries over:
const myInstance2 = Singleton.getInstance();
console.log(myInstance2.temperature); // 26
Compare the two and you get true, because they’re the same object:
console.log(myInstance === myInstance2); // true