Creational patterns deal with object creation. Singleton, Factory, and Builder are the three you will use most. Today you will implement all three in TypeScript and understand when to apply each.
// Singleton — one instance shared across the app
class Database {
private static instance: Database;
private constructor(private url: string) {}
static getInstance(): Database {
if (!Database.instance) {
Database.instance = new Database(process.env.DATABASE_URL!);
}
return Database.instance;
}
query(sql: string) { /* ... */ }
}
const db1 = Database.getInstance();
const db2 = Database.getInstance();
console.log(db1 === db2); // true// Factory Method — delegate instantiation to subclasses
interface Button { render(): string; }
class PrimaryButton implements Button {
render() { return '<button class="btn-primary">Click</button>'; }
}
class DangerButton implements Button {
render() { return '<button class="btn-danger">Delete</button>'; }
}
function createButton(type: 'primary' | 'danger'): Button {
if (type === 'primary') return new PrimaryButton();
return new DangerButton();
}// Builder — construct complex objects step by step
class QueryBuilder {
private table = '';
private conditions: string[] = [];
private limitVal: number | null = null;
from(table: string) { this.table = table; return this; }
where(condition: string) { this.conditions.push(condition); return this; }
limit(n: number) { this.limitVal = n; return this; }
build(): string {
let sql = \`SELECT * FROM \${this.table}\`;
if (this.conditions.length)
sql += \` WHERE \${this.conditions.join(' AND ')}\`;
if (this.limitVal !== null)
sql += \` LIMIT \${this.limitVal}\`;
return sql;
}
}
const query = new QueryBuilder()
.from('users')
.where('active = true')
.where('age > 18')
.limit(10)
.build();
// SELECT * FROM users WHERE active = true AND age > 18 LIMIT 10