← Back to TypeScript Course | Chapter 16: Advanced Patterns | Lesson 3 of 7

Singleton Pattern

The Singleton Pattern ensures that a class has only one shared instance. It is useful when one centralized object is intentionally shared throughout an application.

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

typescript
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

typescript
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

typescript
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

typescript
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

typescript
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:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.