How to define Singleton in TypeScript

前端 未结 20 667

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

相关标签:
20条回答
  • 2020-11-28 20:35

    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;
      }
    }
    
    0 讨论(0)
  • 2020-11-28 20:40

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

    0 讨论(0)
  • 2020-11-28 20:41

    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.

    0 讨论(0)
  • 2020-11-28 20:41

    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
    
    0 讨论(0)
  • 2020-11-28 20:44

    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
    
    0 讨论(0)
  • 2020-11-28 20:44

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

    0 讨论(0)
提交回复
热议问题