S

Singleton Pattern

The Singleton pattern ensures a class has only one instance throughout the application.

Code

typescript
1// Database connection singleton
2class Database {
3 private static instance: Database | null = null;
4 private connected: boolean = false;
5
6 // Private constructor prevents direct instantiation
7 private constructor(private connectionString: string) {}
8
9 // Static method to get the single instance
10 static getInstance(connectionString?: string): Database {
11 if (!Database.instance) {
12 if (!connectionString) {
13 throw new Error("Connection string required for first initialization");
14 }
15 Database.instance = new Database(connectionString);
16 }
17 return Database.instance;
18 }
19
20 async connect(): Promise<void> {
21 if (this.connected) {
22 console.log("Already connected");
23 return;
24 }
25
26 console.log(`Connecting to ${this.connectionString}...`);
27 // Simulated connection
28 await new Promise((resolve) => setTimeout(resolve, 100));
29 this.connected = true;
30 console.log("Connected!");
31 }
32
33 async query(sql: string): Promise<unknown[]> {
34 if (!this.connected) {
35 throw new Error("Not connected to database");
36 }
37 console.log(`Executing: ${sql}`);
38 return []; // Simulated result
39 }
40
41 async disconnect(): Promise<void> {
42 if (!this.connected) return;
43 console.log("Disconnecting...");
44 this.connected = false;
45 }
46
47 // For testing - reset the singleton
48 static resetInstance(): void {
49 Database.instance = null;
50 }
51}
52
53// Configuration singleton using a class
54class Config {
55 private static instance: Config;
56 private settings: Map<string, unknown> = new Map();
57
58 private constructor() {
59 // Load default settings
60 this.settings.set("apiUrl", "https://api.example.com");
61 this.settings.set("timeout", 5000);
62 this.settings.set("debug", false);
63 }
64
65 static getInstance(): Config {
66 if (!Config.instance) {
67 Config.instance = new Config();
68 }
69 return Config.instance;
70 }
71
72 get<T>(key: string): T | undefined {
73 return this.settings.get(key) as T | undefined;
74 }
75
76 set<T>(key: string, value: T): void {
77 this.settings.set(key, value);
78 }
79}
80
81// Usage example
82async function main(): Promise<void> {
83 // Database singleton
84 const db1 = Database.getInstance("postgres://localhost:5432/mydb");
85 const db2 = Database.getInstance(); // Returns same instance
86
87 console.log("Same instance?", db1 === db2); // true
88
89 await db1.connect();
90 await db1.query("SELECT * FROM users");
91
92 // Config singleton
93 const config = Config.getInstance();
94 console.log("API URL:", config.get<string>("apiUrl"));
95
96 config.set("debug", true);
97 console.log("Debug mode:", config.get<boolean>("debug"));
98}
99
100main().catch(console.error);

Run this example locally

$ npx ts-node patterns/singleton.ts