Singleton Pattern
Basic Singleton
A basic singleton exposes exactly one shared instance of a class through a static method or property, ensuring every part of the program that asks for it gets back the exact same object.
Example: Basic Singleton
class Config {
private static instance: Config;
static getInstance(): Config {
if (!Config.instance) Config.instance = new Config();
return Config.instance;
}
}
console.log(Config.getInstance() === Config.getInstance());
Private Constructor
A private constructor is what actually enforces the singleton guarantee — since new can't be called on the class from outside, the only way to get an instance is through the class's own static accessor.
Example: Private Constructor
class Config {
private static instance: Config;
private constructor() {}
static getInstance(): Config {
if (!Config.instance) Config.instance = new Config();
return Config.instance;
}
}
console.log(Config.getInstance());
Shared State
Shared state is the whole point of a singleton — because every consumer holds a reference to the same instance, changes made through one reference are immediately visible through every other reference too.
Example: Shared State
class Counter {
private static instance: Counter;
count = 0;
static getInstance(): Counter {
if (!Counter.instance) Counter.instance = new Counter();
return Counter.instance;
}
}
Counter.getInstance().count = 5;
console.log(Counter.getInstance().count);
Lazy Creation
Lazy creation means the singleton's one instance is only constructed the first time it's actually requested, not immediately when the module loads, which avoids unnecessary work if the singleton ends up unused.
Example: Lazy Creation
class Heavy {
private static instance: Heavy;
private constructor() { console.log("Created!"); }
static getInstance(): Heavy {
if (!Heavy.instance) Heavy.instance = new Heavy();
return Heavy.instance;
}
}
console.log("Before first use");
Heavy.getInstance();
When to Use Singleton
Reach for a singleton only when truly one shared instance is required program-wide, like a single configuration object or connection pool — overusing singletons for ordinary objects makes testing and reasoning about state much harder.
Example: When to Use Singleton
class Logger {
private static instance: Logger;
static getInstance(): Logger {
if (!Logger.instance) Logger.instance = new Logger();
return Logger.instance;
}
log(msg: string) { console.log("[LOG]", msg); }
}
Logger.getInstance().log("App started");
Chapter Quiz — Complete all 7 topics to unlock
0/7 topics done
Complete these topics first: