Design Patterns in TypeScript with Examples: From A to Z

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Design Patterns in TypeScript: From Simple to Complex



Design patterns are time-tested solutions to typical problems in software development. In the context of TypeScript, which adds strict typing and OOP capabilities to JavaScript, patterns become especially effective. TypeScript allows you to implement abstractions, interfaces, and polymorphism at a new level, making code more predictable and safe.



In this article, we will cover key design patterns, their implementation in TypeScript, and practical usage examples. The material will be useful for both beginner developers and experienced professionals looking to systematize their knowledge.



Creational Patterns: Singleton and Factory Method



Creational patterns are responsible for creating objects, hiding the instantiation logic. Let's look at two of the most popular ones: Singleton and Factory Method.



Singleton



Singleton guarantees that a class has only one instance and provides a global access point to it. In TypeScript, the implementation becomes especially elegant thanks to static members and private constructors.



class DatabaseConnection {    private static instance: DatabaseConnection;    private connectionString: string;

private constructor(connectionString: string) { this.connectionString = connectionString; console.log(`Connection created to: ${connectionString}`); }

static getInstance(connectionString: string): DatabaseConnection { if (!DatabaseConnection.instance) { DatabaseConnection.instance = new DatabaseConnection(connectionString); } return DatabaseConnection.instance; }

query(sql: string): void { console.log(`Executing query: ${sql} on ${this.connectionString}`); }}

// Usageconst db1 = DatabaseConnection.getInstance('postgres://localhost:5432/mydb');const db2 = DatabaseConnection.getInstance('postgres://localhost:5432/mydb');console.log(db1 === db2); // true - the same object


This pattern is ideal for managing resources: connection pools, logging, caching. However, be careful — Singleton can complicate testing due to global state.



Factory Method



Factory Method defines an interface for creating an object but allows subclasses to alter the type of object that will be created. In TypeScript, this is implemented through abstract classes or interfaces.



interface Notification {    send(message: string): void;}

class EmailNotification implements Notification { send(message: string): void { console.log(`Sending email: ${message}`); }}

class SMSNotification implements Notification { send(message: string): void { console.log(`Sending SMS: ${message}`); }}

abstract class NotificationFactory { abstract createNotification(): Notification;

notify(message: string): void { const notification = this.createNotification(); notification.send(message); }}

class EmailFactory extends NotificationFactory { createNotification(): Notification { return new EmailNotification(); }}

class SMSFactory extends NotificationFactory { createNotification(): Notification { return new SMSNotification(); }}

// Usageconst factory: NotificationFactory = new EmailFactory();factory.notify('Your order has been confirmed!');


Factory Method helps adhere to the Open/Closed Principle (OCP) — you can add new notification types without changing existing code.



Structural Patterns: Adapter and Decorator



Structural patterns help organize relationships between objects and classes, making the system more flexible and extensible.



Adapt

Blogs

Book Recommendations