Skip to main content

What It Is

The Singleton pattern makes sure a class has exactly one instance in the whole program, and gives everyone the same access point to it. Typical singletons: a configuration, a connection pool, a logger.

TypeScript gets this almost for free. A module is loaded and run once, and everything it creates is cached and shared by every later import. So a value stored at module level — including a static field on a class — already exists exactly once. The pattern only adds two things: a private constructor, so nobody can build a second one, and a static accessor that creates the value on first use.


Example: One Config for Everyone

The config is loaded on the first Config.getInstance() call and never again. Every caller receives the same object.

Load once, keep it in a static field

The "loading config" line prints only once, although Config.getInstance() is called twice — and a === b proves both callers hold the same object. The ??= operator assigns only when the field is still undefined, which is exactly "create it the first time".

Prompt
Write a TypeScript program with a Config singleton: a class with a private constructor that prints a line while loading, and a static getInstance method that creates the instance on first call and returns the cached one afterwards. Call getInstance twice, print the app name, and compare the two results with === to prove they are the same object.
Output: TypeScript file and captured output

When to Use It

Use it when there really must be one of something: one loaded configuration, one connection pool, one cache — and creating it is expensive or duplicates would conflict.

Skip it when you can simply create the value once at the top of your program and pass it to whoever needs it. Passing dependencies openly is clearer than global access — the singleton is the last resort, not the first.


Pros and Cons

Pros: guaranteed single instance; the private constructor makes a second one impossible; creation is lazy, so nothing is built until someone asks.

Cons: it is a global in disguise — hidden dependencies make code harder to follow; tests cannot easily swap it for a fake, and one test can leave state behind for the next; overuse leads to tangled programs.