Creating a Singleton in TypeScript
A classless object approach to a well-known pattern — from JavaScript to TypeScript

Often the design patterns are translated to JavaScript using the ES6 Class declaration, perhaps for a couple of reasons:
- Popular definitions¹ of design patterns use the class keyword
- Java proliferates, and it is trivial to transfer existing patterns to JavaScript Classes (Design Patterns² emerged in 1994 and quickly became popular in Java a few years later)
JavaScript is multi-paradigm and can also support class and non-class objects within the same codebase (a class declaration is a syntactic sugar in JavaScript).
However, there is a general consensus that a class-free approach is better for various reasons³. Additionally, if you are using React and have migrated to Hooks⁴ since v16.8, you’ve probably stopped using classes or at least adding new ones.
What is a Singleton
Simply put, a Singleton is an object that can have only one instance.
It’s useful when sharing a single version of data and/or functions across multiple modules is needed. Perhaps you want to have the Authorisation Credentials declared once or create an Axios instance with decorated methods. It’s also very popular when creating a Logging instance.
If you’re using React, you can also create a Singleton simply by using the useContext hook. However, it is still educational and interesting to know how to construct Singletons from first principles.
A Naive Implementation
So, how could we create one?
The interface is straightforward. We just need a getInstance()
function that will return one object instance. Repeated calls to this function (including across modules) must return the same instance too.
const singleton = () => ({ name: "I am not yet a singleton 😿" })
/** Should return _the_ instance of a singleton, not new ones 😕 */const getInstance = () => singleton();
But we still need to be able to get the singleton. And only one version of it. So this falls short of the implementation requirements:
- Only can have one instance (
getInstance()
will create a new instance each time) - Global accessor (only accessible within the script)
- Lazy initialisation is good (created when
getInstance()
called)
That’s fine, though. The above example was just a trial approach to get started.
An Improved JavaScript Implementation
Object closure
At this point, we could use a closure to store the instance of the singleton. All definitions of JavaScript closures seem to concentrate on the lexical scope of functions, but it also applies to objects! In our case, the singleton can be stored inside an outer function and made available to an inner object that is returned. Here’s the code:
I’m highlighting the above approach as a solution if you want to create a private yet accessible value within a script, but there is still room for improvement for our purposes.
Module scope
Modules were introduced in ES6 as script files that run in strict mode only, preventing functions and variables from appearing in the global scope.
Interestingly, modules are singletons; no matter how often they are imported, they are executed only once.
As a simple example, this works. As soon as the module is imported, the Immediately Invoked Function Expression (IIFE) will run and create a single instance. If we access it via getInstance()
, we’ll always get the same singleton object.
However, you might have noticed a problem:
- Only can have one instance (module IIFE has done this for us)
- Global accessor (accessible via the exported getInstance() — only need to import to any module that requires access to the singleton)
- Lazy initialisation is not good (it’s created as soon as the module is imported)
We can take advantage of the module scope to “hide” our singleton instance and control access to it, and we can discard the IIFE, which is no longer necessary:
OK, that resolves our issue with the Lazy Initialisation, and we now have something useful.
- Only can have one instance
- Global accessor
- Lazy initialisation (created on the first reference)
One potential issue here is that the singleton returned can be modified by the client, so we can resolve that in JavaScript by freezing the singleton object:
But what if we want to take this further?
We haven’t yet introduced typings for a TypeScript implementation, so let’s do that. Also, what if we’d like to optionally initialise the Singleton with a value?
A TypSscript Implementation
The first step is to create some type for the Singleton’s value and make it accessible externally (since dependent functions will probably want to pass it around and include it in their typings)
/**
* Type of whatever we want to store in the singleton
* In this case, it is an object with a name attribute
*/export type SingletonValue = {name: string};
The actual type I’ve used is arbitrary but would be specific to the singleton’s usage unless we want a singleton builder, but that’s for another discussion.
The singleton itself can be generic, and as such, it could be extracted to a separate library if so desired. For now, I’ll keep it within the same module for simplicity.
Keeping idiomatic with TypeScript, we define a <T> generic for its contained value.
I’ve removed the Object.freeze()
from the implementation to illustrate how compile-time TypeScript will help defend the implementation. If you’re using it within your code base, that should be fine, but when implementing a third-party library, you’d probably want to freeze it just in case.
Now we can include a private “instance” for our singleton within the module (recall that modules always run in strict mode, so any values will not be accessible unless we explicitly export them)
/**
* The only instance of our Singleton
*/
let instance: ReturnType<typeof makeSingleton<SingletonValue>>;
This uses a “Utility Type” called Return Type and specifies the type of the Singleton Value that it will store (i.e., the return type of the makeSingleton
function when using a value of SingletonValue
)
The final step is to handle an optional initialisation and specify the type of Singleton we’re creating. Here’s the code:
However, there is now a small issue that the initial value could be passed each time we subsequently call getInstance()
. It’s not all that problematic since it is just ignored, but it might result in a misunderstanding.
The best approach is to throw an exception if the Singleton is already created. Here’s the code:
So we now have reached a final version that meets our requirements. Here’s what it looks like:
Usage of the Singleton
From any module, we can simply obtain the getInstance
function:
import getInstance from "./Singleton";const singleton = getInstance();singleton.setValue({ name: "what is in a name" });const theNameObj = singleton.getValue();
Obtaining multiple references to the singleton will result in the same underlying object. Here’s the code:
const singletonRef1 = getInstance();
const singletonRef2 = getInstance();console.log(singletonRef1 === singletonRef2); // true
Additionally, extending the Singleton is impossible since TypeScript will balk. Here’s the code:
singleton.myExtraFunc = () => { /** some logic */ }Property 'myExtraFunc' does not exist on type '{ getValue: () => Singleton | undefined; setValue: (value: Singleton) => Singleton; }'.ts(2339)
Wrap Up
Hopefully, you can see this article is more than just about implementing a Singleton in TypeScript and covers many interesting topics (closures, modules, IIFEs, generics, etc.)
Did you find this interesting?
What do you think of the final implementation? What would you do to improve it?
Let me know your feedback in the comments below!
Resources
[1]: Wikipedia. Singleton Pattern. https://en.wikipedia.org/wiki/Singleton_pattern
[2]: Wikipedia. Design Patterns. https://en.wikipedia.org/wiki/Design_Patterns
[3]: Douglas Crockford. Classical Inheritance in JavaScript. https://www.crockford.com/javascript/inheritance.html
[4]: ReactJs. Hooks FAQ. https://reactjs.org/docs/hooks-faq.html