228

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

0

25 Answers 25

299

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.

Sign up to request clarification or add additional context in comments.

18 Comments

the constructor could be private?
@Expertwannabe This is now available in TS 2.0: github.com/Microsoft/TypeScript/wiki/…
fyi, the reason for the multiple instances was the node module resolution getting in the way. Thus, if you are creating a singleton in node make sure that that's considered. I ended up creating a node_modules folder under my src directory and putting the singleton in there.
@KimchiMan If the project is ever used in a non-typescript environment e.g. imported into a JS project, the class will have no protection against further instantiation. It works in a pure TS environment only, but not for JS library development
@lony Just make Instance a normal static method (remove 'get') that takes the arguments needed from where you pass those to the constructor that also takes the arguments needed.
|
111

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

15 Comments

it would be nice to now why is the singleton is considered an anti pattern? consider this approach codebelt.com/typescript/typescript-singleton-pattern
I'd like to know why Singletons in TypeScript are considered an anti-pattern too. And also if it doesn't have any constructor parameters why not export default new Singleton()?
The namespace solution looks more like a static class, not a singleton
A limitation of using a namespace as a singleton is that it cannot (to my knowledge) implement an interface. Would you agree with this @ryan
As far as I can tell it is now namespaces which are the antipattern. See this eslint rule: github.com/typescript-eslint/typescript-eslint/blob/v2.34.0/…
|
58

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/

5 Comments

Why not make the constructor private?
I think the post predates the ability to have private constructors in TS. github.com/Microsoft/TypeScript/issues/2341
The link is broken. I think this is the actual link: codebelt.github.io/blog/typescript/typescript-singleton-pattern
It's better place "new SingletonClass()" in method "getInstance", otherwise constructor will excute at the location of import this class which we don't expect that happens in some situation.
Every new SingletonClass() instance will change, so it is not usable and not acceptable in singleton structure philosophy
36

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();

5 Comments

This is the way it should be implemented. If there's 1 thing I disagree with The Gang of Four on -- and it's probably only 1 -- its The Singleton Pattern. Perhaps, C/++ impedes one from designing it this way. But if you ask me, client code should not know or care if its a Singleton. Clients should still implement new Class(...) syntax.
Disagree with Cody a new instance must be a new instance, otherwise devs will do wrong assumptions.
I don't entirely agree with the above statement (Juan). getInstance() here has some side effects (it creates a new object) when its name suggests it shouldn't at all (getXXX). I believe it's even worst.
You can use private constructors in typescript.
@HakanDemir not in 2015 :P
32

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();

10 Comments

I got the following error message: Exported variable 'Shout' has or is using private name 'ShoutSingleton '.
You have to export the class 'ShoutSingleton' too, and the error disappear.
Right, I am surprised as well. Why even bother with the class though? Singletons are supposed to hide their internal workings. Why not just export function helloWorld?
Guess nothing is stopping users from just creating a new Shout class though
The problem with this solution export let Shout = new ShoutSingleton(); is that it's not the Singleton pattern because it doesn't fulfill the Singleton's objective of 'Ensuring that a class has only one instance.' This is because every time you import it, you create new instances instead of reusing the same one. In short, for it to be a Singleton, you would need to ensure that the same instance is always returned, not a new one. This is resolved by adding 'Lazy Initialization.'
|
17

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.

[Edit 2]: Bonus: if you want to generate a singleton from your existing class in one line, you can use this class builder:

function AsSingleton<T extends new (...args: any[]) => any>(Aclass: T, ...args: ConstructorParameters<T>) {
  return class extends Aclass {
    static #instance: InstanceType<T>;
    public static getInstance(): InstanceType<T> {
      if (!this.#instance) {
        this.#instance = new Aclass(...args);
      }
      return this.#instance;
    }
  } as Omit<T,"new"> & {getInstance:()=>InstanceType<T>};
}

Check this snippet for an example (even more advanced)

usage: const AClassSingleton = AsSingleton(AClass);

5 Comments

What happens when I do new MySingleton() , say 5 times? does your code reserve a single instance?
you should never use "new": as Alex wrote, the constructor should be "private", preventing to do "new MySingleton()". The right usage is to get an instance using MySingleton.getInstance(). AKAIK no constructor (like in my example) = a public empty constructor
"you should never use "new" - exactly my point:". But how does your implementation prevent me from doing so? I do not see anywhere where you have a private constructor in your class?
@HlawulekaMAS I did not… I therefore I edited the answer, note that a private constructor was not possible before TS 2.0 (i.e. at the time I wrote the answer first)
"i.e. at the time I wrote the answer first" - Makes sense. Cool.
8

2025 Update

Static Factory/Construtor Only Pattern (SFOP)

The purpose of this pattern is to restrict the creation of instances of a class exclusively through static factory methods, preventing direct use of the class constructor. This is useful to ensure that all instances are created with specific configurations or defined validations, allowing easy creation of singletons, object pools, asynchronous constructors, and more.

In Javascript/TypeScript, this can be implemented with the following steps:

  1. StaticConstructorOnlyPattern.new(constructor, ...args): Method that adds a symbolic key to the target class, indicating that the next constructor call is allowed. It then creates and returns a new instance of the class with the provided arguments.
  2. StaticConstructorOnlyPattern.throw(constructor): Method that checks whether the symbolic key is defined. If it is not, it throws an error indicating that the constructor should not be called directly; however, if the key is present, it removes the key to avoid improper reuse and allows the constructor to proceed with instance creation.
// PatterStaticFactory.ts

const comparekey = Symbol()

function $throw(Self: Function) {
  if ((Self as any)[comparekey])
    return void Reflect.set(Self, comparekey, false)
  const constructor = Self.prototype.constructor.name
  throw `new ${constructor}() is not allowed. Use ${constructor}.new() instead.`
}

function $new<A extends any[] = []>(Self: Function, ...args: A) {
  Reflect.set(Self, comparekey, true)
  return new (Self as any)(...args)
}

export const StaticConstructorOnlyPattern = { 
  throw: $throw, 
  new: $new 
}

In the example below, the SfopClass uses the Static Factory Only Pattern. This ensures that instances can only be created through the static method SfopClass.new(...), and any attempt to use the constructor directly with new SfopClass(...) will result in an error. This is done by following these steps:

  1. StaticConstructorOnlyPattern.throw(constructor) is executed in the target class constructor to check whether the instance has permission to be created. This permission is granted only when the static method StaticConstructorOnlyPattern.new(...) is called.

    • If the permission is not present, an error is thrown, preventing direct instance creation.
    • If the permission is present, the method StaticConstructorOnlyPattern.throw(...) removes the symbolic key to avoid improper reuse and allows the constructor to proceed with instance creation.
  2. StaticConstructorOnlyPattern.new<CtorArgs>(this, ...args) adds the required permission and then creates the class instance.

  • This process works because constructors cannot be asynchronous. Thus, only the method StaticConstructorOnlyPattern.new(...) can assign the permission before the constructor call, and StaticConstructorOnlyPattern.throw(...) can check this permission, allowing or denying instance creation and removing the permission immediately afterward to avoid improper reuse.
// main.ts

import { StaticConstructorOnlyPattern as sfop } from "./PatterStaticFactory"

type CtorArgs = 
  | [a: number, b: string]
  | [a: string, b: number]
  | [a: boolean, b: boolean]

class SfopClass {
  protected constructor(...args: CtorArgs) { 
    sfop.throw(new.target)
    console.log("Z constructor")
  }

  static new(...args: CtorArgs) {
    return sfop.new<CtorArgs>(this, ...args)
  }
}

// Factory Calls
SfopClass.new('str', 1)
SfopClass.new(1, 'str')
SfopClass.new(true, true)

Example of how to use the SFOP pattern to create a Singleton class:

// singleton.ts

import { StaticConstructorOnlyPattern as sfop } from "./StaticConstructorOnly"

class Singleton {
  static #instance: Singleton | undefined

  protected constructor() { 
    sfop.throw(new.target)
  }

  static createInstance() {
    return this.#instance ??= sfop.new(this)
  }
}

// Usage
const obj1 = Singleton.createInstance()
const obj2 = Singleton.createInstance()

console.log(obj1 === obj2) // true

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

My early solution:

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;
    }
}

1 Comment

In the constructor, instead of the exception you can return Modal._instance. This way, if you new that class, you get the existing object, not a new one.
7

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();

Comments

7

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();

1 Comment

For Typescript 3.0 and above, you can use type 'unknown' for the return type of static Instance method and the type of _instance, then use like MapManager.Instance(MapManager) as MapManager. This could eliminate the type mismatch error of eslint.
3

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;

5 Comments

Kenny, good point on freeze(), but two notes: (1) after you freeze(singleton), you still can modify singleton.data.. you can't add other attribute (like data2), but point is that freeze() is not deep freeze :) and (2) your class Singleton allows to create more than one instance (example obj1 = new Singleton(); obj2 = new Singleton();), so your Singleton is not Singleton : )
If you import the Singleton Class in other files you will always get the same instance and the data in 'data' will be consistent between all other imports. That's for me a singleton. The freeze in making sure that the exported Singleton instance is only created once.
Kenny, (1) if you import your class in other files you won't get instance. By import you're simply bringing class definition in scope so you can create new instances. Then you can create >1 instances of the given class be it in one file or multiple files, which defies the whole purpose of singleton idea. (2) From docs: The Object.freeze() method freezes an object. A frozen object can no longer be changed; freezing an object prevents new properties from being added to it. (end of quote) Which means freeze() does not stop you from creating multiple objects.
True, but in this case it will, because the exported member is already an instance. And the instance keeps the data. If you put an export on the class as well, your are right and you could create multiple instances.
@kenny if you know you're going to export an instance, why bother with the if (!this.instance) in the constructor? Is that just an extra precaution in case you created multiple instances before the export?
2

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.

Comments

2

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.

Comments

2
/**
 * 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();

3 Comments

How does this answer differ from all the others that provide a getInstance() method for lazy initialization?
It has better explanation of each step of the code. And why does it matter if I added another answer that has overlap in the solution?
I agree that yours is nicely formatted and commented. But it matters, because this question already has 23 answers and new ones are coming in monthly. Instead of duplicating existing answers, it would be best to comment on already existing ones to clarify them or ask questions. Or edit existing answers if they can be improved.
1

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.

Comments

1

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

Comments

1

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.

Comments

1

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()

1 Comment

Why would someone downvote an answer without actually commenting on improvements? We're a community
0

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

2 Comments

What is the reason to add the Instance variable? You coukd simply put the variable and the functions directly under App.Counter.
@fyaa Yes you could but the variable and the functions directly under App.Counter but I think this approach conforms better to the singleton pattern en.wikipedia.org/wiki/Singleton_pattern.
0

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

Comments

0

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;
  }
}

Comments

0

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)
}

Comments

0

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);

Comments

0
class Singleton<CREATOR extends () => any>
{
  private m_fnGetInstance: () => ReturnType<CREATOR>;
  public get Instance() { return this.m_fnGetInstance(); }
  public constructor(creator: CREATOR) {
    this.m_fnGetInstance = () => {
      const instance = creator();
      this.m_fnGetInstance = () => instance;
      return instance;
    };
  }
}

const Foo = new Singleton(() => new class {
  public bar() {}
});

Foo.Instance.bar();

It is fast because there is no conditional branching.

Playground

github

Comments

0

Template One

class Singleton {
        
        /**
         * private : Making instance private ensuring that it can only be accessed with Singleton class itself.
         * static : Making instance static means it belongs to class itself rather than instances of the classes.  This ensures that there is only one instance of "instance" shared across all instances of the class
         */
        private static instance: Singleton;
    
        /**
         * private : Making the constructor "private" prevents instantiation of the class from outside.
         */
        private constructor() {}
    
    
        public static getInstance(): Singleton {
            if(!Singleton.instance){
                Singleton.instance = new Singleton();
            }
            return Singleton.instance;
        }
    }

Template Two (Early Loading)

class DatabaseConnection{
    private static instance = new DatabaseConnection();
    private constructor() {};
    public static getInstance(): DatabaseConnection {
        return DatabaseConnection.instance;
    }
}

Template Three

class DatabaseConnection{
    private static instance : DatabaseConnection;
    private constructor(){};
    public static getInstance(): DatabaseConnection{
        if(!DatabaseConnection.instance){
            DatabaseConnection.instance = new DatabaseConnection();
        }
        return DatabaseConnection.instance;
    }
}

1 Comment

Template 1 and 3 are identical, and the answer as a whole does not add anything new to the already existing answers.
-1
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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.