How to define Singleton in TypeScript

SingletonTypescript

Singleton Problem Overview


What is the best and most convenient way to implement a Singleton pattern for a class in TypeScript? (Both with and without lazy initialisation).

Singleton Solutions


Solution 1 - Singleton

Since TS 2.0, we have the ability to define visibility modifiers on constructors, so now we can do singletons in TypeScript just like we are used to from other languages.

Example given:

class MyClass
{
    private static _instance: MyClass;

    private constructor()
    {
        //...
    }

    public static get Instance()
    {
        // Do you need arguments? Make it a regular static method instead.
        return this._instance || (this._instance = new this());
    }
}

const myClassInstance = MyClass.Instance;

Thank you @Drenai for pointing out that if you write code using the raw compiled javascript you will not have protection against multiple instantiation, as the constraints of TS disappears and the constructor won't be hidden.

Solution 2 - Singleton

Singleton classes in TypeScript are generally an anti-pattern. You can simply use namespaces instead.

Useless singleton pattern
class Singleton {
    /* ... lots of singleton logic ... */
    public someMethod() { ... }
}

// Using
var x = Singleton.getInstance();
x.someMethod();
Namespace equivalent
export namespace Singleton {
    export function someMethod() { ... }
}
// Usage
import { SingletonInstance } from "path/to/Singleton";

SingletonInstance.someMethod();
var x = SingletonInstance; // If you need to alias it for some reason

Solution 3 - Singleton

The best way I have found is:

class SingletonClass {
 
    private static _instance:SingletonClass = new SingletonClass();
 
    private _score:number = 0;
 
    constructor() {
        if(SingletonClass._instance){
            throw new Error("Error: Instantiation failed: Use SingletonClass.getInstance() instead of new.");
        }
        SingletonClass._instance = this;
    }
 
    public static getInstance():SingletonClass
    {
        return SingletonClass._instance;
    }
 
    public setScore(value:number):void
    {
        this._score = value;
    }
 
    public getScore():number
    {
        return this._score;
    }
 
    public addPoints(value:number):void
    {
        this._score += value;
    }
 
    public removePoints(value:number):void
    {
        this._score -= value;
    }
 
}

Here is how you use it:

var scoreManager = SingletonClass.getInstance();
scoreManager.setScore(10);
scoreManager.addPoints(1);
scoreManager.removePoints(2);
console.log( scoreManager.getScore() );

https://codebelt.github.io/blog/typescript/typescript-singleton-pattern/

Solution 4 - Singleton

The following approach creates a Singleton class that can be used exacly like a conventional class:

class Singleton {
    private static instance: Singleton;
    //Assign "new Singleton()" here to avoid lazy initialisation
    
    constructor() {
        if (Singleton.instance) {
            return Singleton.instance;
        }

        this. member = 0;
        Singleton.instance = this;
    }
    
    member: number;
}

Each new Singleton() operation will return the same instance. This can however be unexpected by the user.

The following example is more transparent to the user but requires a different usage:

class Singleton {
    private static instance: Singleton;
    //Assign "new Singleton()" here to avoid lazy initialisation
    
    constructor() {
        if (Singleton.instance) {
            throw new Error("Error - use Singleton.getInstance()");
        }
        this.member = 0;
    }

    static getInstance(): Singleton {
        Singleton.instance = Singleton.instance || new Singleton();
        return Singleton.instance;
    }
    
    member: number;
}

Usage: var obj = Singleton.getInstance();

Solution 5 - Singleton

I am surprised not to see the following pattern here, which actually looks very simple.

// shout.ts
class ShoutSingleton {
  helloWorld() { return 'hi'; }
}

export let Shout = new ShoutSingleton();

Usage

import { Shout } from './shout';
Shout.helloWorld();

Solution 6 - Singleton

Add the following 6 lines to any class to make it "Singleton".

class MySingleton
{
    private constructor(){ /* ... */}
	private static _instance: MySingleton;
	public static getInstance(): MySingleton
	{
		return this._instance || (this._instance = new this());
	};
}

Test example:

var test = MySingleton.getInstance(); // will create the first instance
var test2 = MySingleton.getInstance(); // will return the first instance
alert(test === test2); // true

[Edit]: Use Alex answer if you prefer to get the instance through a property rather a method.

Solution 7 - Singleton

You can use class expressions for this (as of 1.6 I believe).

var x = new (class {
    /* ... lots of singleton logic ... */
    public someMethod() { ... }
})();

or with the name if your class needs to access its type internally

var x = new (class Singleton {
    /* ... lots of singleton logic ... */
    public someMethod(): Singleton { ... }
})();

Another option is to use a local class inside of your singleton using some static members

class Singleton {
				
    private static _instance;
    public static get instance() {
	
	    class InternalSingleton {
		    someMethod() { }
		
		    //more singleton logic
	    }
	
	    if(!Singleton._instance) {
		    Singleton._instance = new InternalSingleton();
	    }
	
	    return <InternalSingleton>Singleton._instance;
    }
}

var x = Singleton.instance;
x.someMethod();

Solution 8 - Singleton

My solution for it:

export default class Singleton {
    private static _instance: Singleton = new Singleton();

    constructor() {
        if (Singleton._instance)
            throw new Error("Use Singleton.instance");
        Singleton._instance = this;
    }

    static get instance() {
        return Singleton._instance;
    }
}

2021 update

Now constructor can be private

export default class Singleton {
    private static _instance?: Singleton;

    private constructor() {
        if (Singleton._instance)
            throw new Error("Use Singleton.instance instead of new.");
        Singleton._instance = this;
    }

    static get instance() {
        return Singleton._instance ?? (Singleton._instance = new Singleton());
    }
}

Test in TS Playground

Solution 9 - Singleton

i think maybe use generics be batter

class Singleton<T>{
    public static Instance<T>(c: {new(): T; }) : T{
        if (this._instance == null){
            this._instance = new c();
        }
        return this._instance;
    }

    private static _instance = null;
}

how to use

step1

class MapManager extends Singleton<MapManager>{
     //do something
     public init():void{ //do }
}

step2

    MapManager.Instance(MapManager).init();

Solution 10 - Singleton

You can also make use of the function Object.Freeze(). Its simple and easy:

class Singleton {

  instance: any = null;
  data: any = {} // store data in here

  constructor() {
    if (!this.instance) {
      this.instance = this;
    }
    return this.instance
  }
}

const singleton: Singleton = new Singleton();
Object.freeze(singleton);

export default singleton;

Solution 11 - Singleton

I have found a new version of this that the Typescript compiler is totally okay with, and I think is better because it doesn't require calling a getInstance() method constantly.

import express, { Application } from 'express';

export class Singleton {
  // Define your props here
  private _express: Application = express();
  private static _instance: Singleton;

  constructor() {
    if (Singleton._instance) {
      return Singleton._instance;
    }

    // You don't have an instance, so continue

    // Remember, to set the _instance property
    Singleton._instance = this;
  }
}

This does come with a different drawback. If your Singleton does have any properties, then the Typescript compiler will throw a fit unless you initialize them with a value. That's why I included an _express property in my example class because unless you initialize it with a value, even if you assign it later in the constructor, Typescript will think it hasn't been defined. This could be fixed by disabling strict mode, but I prefer not to if possible. There is also another downside to this method I should point out, because the constructor is actually getting called, each time it does another instance is technically created, but not accessible. This could, in theory, cause memory leaks.

Solution 12 - Singleton

/**
 * The Singleton class defines the `getInstance` method that lets clients access
 * the unique singleton instance.
 */
class Singleton {
    private static instance: Singleton;

    /**
     * The Singleton's constructor should always be private to prevent direct
     * construction calls with the `new` operator.
     */
    private constructor() { }

    /**
     * The static method that controls the access to the singleton instance.
     *
     * This implementation let you subclass the Singleton class while keeping
     * just one instance of each subclass around.
     */
    public static getInstance(): Singleton {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }

        return Singleton.instance;
    }

    /**
     * Finally, any singleton should define some business logic, which can be
     * executed on its instance.
     */
    public someBusinessLogic() {
        // ...
    }
}

/**
 * The client code.
 */
function clientCode() {
    const s1 = Singleton.getInstance();
    const s2 = Singleton.getInstance();

    if (s1 === s2) {
        console.log('Singleton works, both variables contain the same instance.');
    } else {
        console.log('Singleton failed, variables contain different instances.');
    }
}

clientCode();

Solution 13 - Singleton

This is probably the longest process to make a singleton in typescript, but in larger applications is the one that has worked better for me.

First you need a Singleton class in, let's say, "./utils/Singleton.ts":

module utils {
    export class Singleton {
        private _initialized: boolean;

        private _setSingleton(): void {
            if (this._initialized) throw Error('Singleton is already initialized.');
            this._initialized = true;
        }

        get setSingleton() { return this._setSingleton; }
    }
}

Now imagine you need a Router singleton "./navigation/Router.ts":

/// <reference path="../utils/Singleton.ts" />

module navigation {
    class RouterClass extends utils.Singleton {
        // NOTICE RouterClass extends from utils.Singleton
        // and that it isn't exportable.

        private _init(): void {
            // This method will be your "construtor" now,
            // to avoid double initialization, don't forget
            // the parent class setSingleton method!.
            this.setSingleton();

            // Initialization stuff.
        }

        // Expose _init method.
        get init { return this.init; }
    }

    // THIS IS IT!! Export a new RouterClass, that no
    // one can instantiate ever again!.
    export var Router: RouterClass = new RouterClass();
}

Nice!, now initialize or import wherever you need:

/// <reference path="./navigation/Router.ts" />

import router = navigation.Router;

router.init();
router.init(); // Throws error!.

The nice thing about doing singletons this way is that you still use all the beauty of typescript classes, it gives you nice intellisense, the singleton logic keeps someway separated and it's easy to remove if needed.

Solution 14 - Singleton

In Typescript, one doesn't necessarily have to follow the new instance() Singleton methodology. An imported, constructor-less static class can work equally as well.

Consider:

export class YourSingleton {

   public static foo:bar;

   public static initialise(_initVars:any):void {
     YourSingleton.foo = _initvars.foo;
   }

   public static doThing():bar {
     return YourSingleton.foo
   }
}

You can import the class and refer to YourSingleton.doThing() in any other class. But remember, because this is a static class, it has no constructor so I usually use an intialise() method that is called from a class that imports the Singleton:

import {YourSingleton} from 'singleton.ts';

YourSingleton.initialise(params);
let _result:bar = YourSingleton.doThing();

Don't forget that in a static class, every method and variable needs to also be static so instead of this you would use the full class name YourSingleton.

Solution 15 - Singleton

After scouring this thread and playing around with all the options above - I settled with a Singleton that can be created with proper constructors:

export default class Singleton {
  private static _instance: Singleton

  public static get instance(): Singleton {
    return Singleton._instance
  }

  constructor(...args: string[]) {
    // Initial setup

    Singleton._instance = this
  }

  work() { /* example */ }

}

It would require an initial setup (in main.ts, or index.ts), which can easily be implemented by
new Singleton(/* PARAMS */)

Then, anywhere in your code, just call Singleton.instnace; in this case, to get work done, I would call Singleton.instance.work()

Solution 16 - Singleton

After implementing a classic pattern like

class Singleton {
  private instance: Singleton;
  
  private constructor() {}

  public getInstance() {
    if (!this.instance) { 
      this.instance = new Singleton();
    }
    return this.instance;
  }
}

I realized it's pretty useless in case you want some other class to be a singleton too. It's not extendable. You have to write that singleton stuff for every class you want to be a singleton.

Decorators for the rescue.

@singleton
class MyClassThatIsSingletonToo {}

You can write decorator by yourself or take some from npm. I found this proxy-based implementation from @keenondrums/singleton package neat enough.

Solution 17 - Singleton

Here is yet another way to do it with a more conventional javascript approach using an IFFE:

module App.Counter {
	export var Instance = (() => {
		var i = 0;
		return {
			increment: (): void => {
				i++;
			},
			getCount: (): number => {
				return i;
			}
		}
	})();
}

module App {
	export function countStuff() {
		App.Counter.Instance.increment();
		App.Counter.Instance.increment();
		alert(App.Counter.Instance.getCount());
	}
}

App.countStuff();

View a demo

Solution 18 - Singleton

Another option is to use Symbols in your module. This way you can protect your class, also if the final user of your API is using normal Javascript:

let _instance = Symbol();
export default class Singleton {

	constructor(singletonToken) {
		if (singletonToken !== _instance) {
            throw new Error("Cannot instantiate directly.");
        }
        //Init your class
    }
    
    static get instance() {
        return this[_instance] || (this[_instance] = new Singleton(_singleton))
    }
    
    public myMethod():string {
        return "foo";
    }
}

Usage:

var str:string = Singleton.instance.myFoo();

If the user is using your compiled API js file, also will get an error if he try to instantiate manually your class:

// PLAIN JAVASCRIPT: 
var instance = new Singleton(); //Error the argument singletonToken !== _instance symbol

Solution 19 - Singleton

Not a pure singleton (initialization may be not lazy), but similar pattern with help of namespaces.

namespace MyClass
{
    class _MyClass
    {
    ...
    }
    export const instance: _MyClass = new _MyClass();
}

Access to object of Singleton:

MyClass.instance

Solution 20 - Singleton

This is the simplest way

class YourSingletoneClass {
  private static instance: YourSingletoneClass;

  private constructor(public ifYouHaveAnyParams: string) {

  }
  static getInstance() {
    if(!YourSingletoneClass.instance) {
      YourSingletoneClass.instance = new YourSingletoneClass('If you have any params');
    }
    return YourSingletoneClass.instance;
  }
}

Solution 21 - Singleton

Lets go by an example, I want to create singleton class by which I will able to create a connection of a client then I want to use that same connected client everywhere.

import nats, { Stan } from 'node-nats-streaming';

class NatsWrapper {
  private _client?: Stan;

  get client() {
    if (!this._client) {
      throw new Error('Cannot access NATS client before connecting');
    }
    return this._client;
  }

  connect(clusterId: string, clientId: string, url: string) {
    this._client = nats.connect(clusterId, clientId, { url });

    return new Promise((resolve, reject) => {
      this.client.on('connect', (result) => {
        console.log('Connected to NATS');
        resolve(result);
      });
      this.client.on('error', (err) => {
        reject(err);
      });
    });
  }
}

// since we create and export the instace, it will act like a singleton
export const natsWrapper = new NatsWrapper();

Now, first create the conneciton in your index.ts/app.ts file then you will be able to access the same client just by importing anywhere

index.ts

    await natsWrapper.connect(
      'ticketing',
      'client_id_random_str',
      'http://nats-srv:4222'
    );

someFile.ts

import { natsWrapper } from '../nats-wrapper';

const abc = () =>{
    console.log(natsWrapper.client)
}

Solution 22 - Singleton

I have struggled to find a proper solution for declaring singleton pattern class in typescript.

I think below is more practical solution.

class MySingletonClass {
    public now:Date = new Date();
    public arg:string;
    constructor(arg:string) {
        this.arg = arg;

        // Make singleton
        if ('instance' in MySingletonClass) return Object.getOwnPropertyDescriptor(MySingletonClass, 'instance')?.value;
        Object.assign(MySingletonClass, { instance: this });
    }
}

const a = new MySingletonClass('a');
console.log(a);

const b = new MySingletonClass('b');
console.log(b);

console.log('a === b', a === b);
console.log('a.now === b.now', a.now === b.now);

Solution 23 - Singleton

namespace MySingleton {
  interface IMySingleton {
      doSomething(): void;
  }
  class MySingleton implements IMySingleton {
      private usePrivate() { }
      doSomething() {
          this.usePrivate();
      }
  }
  export var Instance: IMySingleton = new MySingleton();
}

This way we can apply an interface, unlike in Ryan Cavanaugh's accepted answer.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmajaView Question on Stackoverflow
Solution 1 - SingletonAlexView Answer on Stackoverflow
Solution 2 - SingletonRyan CavanaughView Answer on Stackoverflow
Solution 3 - SingletoncodeBeltView Answer on Stackoverflow
Solution 4 - SingletonmajaView Answer on Stackoverflow
Solution 5 - SingletonRomain BruckertView Answer on Stackoverflow
Solution 6 - SingletonFlavien VolkenView Answer on Stackoverflow
Solution 7 - SingletonbinglesView Answer on Stackoverflow
Solution 8 - SingletonDaniel de Andrade VarelaView Answer on Stackoverflow
Solution 9 - SingletonsanyeView Answer on Stackoverflow
Solution 10 - SingletonkennyView Answer on Stackoverflow
Solution 11 - SingletonEagerestwolfView Answer on Stackoverflow
Solution 12 - SingletonMilkncookiezView Answer on Stackoverflow
Solution 13 - SingletonAdrian GuerreroView Answer on Stackoverflow
Solution 14 - SingletonDominic LeeView Answer on Stackoverflow
Solution 15 - SingletonTheGeekZnView Answer on Stackoverflow
Solution 16 - SingletonMaksim NesterenkoView Answer on Stackoverflow
Solution 17 - SingletonJesperAView Answer on Stackoverflow
Solution 18 - SingletonCibermanView Answer on Stackoverflow
Solution 19 - SingletonsergzachView Answer on Stackoverflow
Solution 20 - SingletonSorin VeștemeanView Answer on Stackoverflow
Solution 21 - SingletonRafiqView Answer on Stackoverflow
Solution 22 - SingletonMargesh PatelView Answer on Stackoverflow
Solution 23 - Singletonuser487779View Answer on Stackoverflow