In TypeScript, like in JavaScript, a singleton template does not exist, because you can know it in Java or in another language.
Why don't we have the same concept?
Because objects and classes are not interdependent.
First, let's ask ourselves what is singleton?
A class that can have only one instance in the entire global application.
In TypeScript, you do this by creating a global variable.
For instance:
// at global scope var instance = { prodMode: true };
or more explicitly or from within the module:
window.instance = { prodMode: true }; declare global { interface Window { instance: { prodMode: boolean }; } }
These are not classes, locks, or defensive measures, but just a global variable.
The above approach has the following additional benefits (only on my head):
- This is the default definition.
- It is very simple.
- The object can be used conveniently and unceremoniously.
Now you can answer what you need or at least want a class.
No problems
window.instance = new class { prodMode = true };
But please do not use classes for simple configuration objects.
Now about your use for setting up the instance:
It depends on the use case:
If you want to have a custom singleton, you should carefully consider your design.
But if, after much deliberation, it seems necessary to create a global design function, consider the following setting
namespace singleton { class Singleton_ {constructor(options: {}){}} export type Singleton = Singleton_; var instance: Singleton = undefined; export function getInstance(options: {}) { instance = instance || new Singleton_(options); return instance; }
The above approach, which uses the IIFE (a TypeScript namespace ) template, has the following advantages:
- There is no need to write a
private constructor to prevent an external instance. - A class cannot be directly created (although it is available as
instance.constructor , which is another reason to exclude the class and use a simple object, as described earlier). - There is no
class that can be extended or otherwise improperly used in any way that breaks a singleton pattern at runtime (although it is available as instance.constructor , which is another reason to avoid the class and use a simple object as described previously) .
But, as you say, passing parameters to the getInstance function makes the API incomprehensible.
If the goal is rather to allow the instance to be modified, simply infer the reconfiguration method (or just modify the object).
window.instance = { prodMode: true, reconfigure(options) { this.prodMode = options.prodMode; } };
Notes:
Some examples of well-known singletones in JavaScript include
- Object object
- Array Object
- Function Object
- (Your favorite library) when loading with script tag
Globals are usually bad, and volatile global values ββare usually worse.
Modules can help here.
Personally, since I use modules, if I needed a different global object, depending on the value of something like βin production,β I would write
// app.ts export {} (async function run() { const singletonConditionalModuleSpecifer = prodMode ? "./prod-singleton" : "./dev-singleton"; const singleton = await import(singletonConditionalModuleSpecifer); // use singleton }());