StructuralTypeScriptverifiedVerified
Bridge Pattern in TypeScript
Decouples an abstraction from its implementation so that the two can vary independently.
How to Implement the Bridge Pattern in TypeScript
1Step 1: Define the implementation interface
interface Renderer {
renderCircle(radius: number): void;
renderSquare(side: number): void;
}2Step 2: Create concrete implementations
class VectorRenderer implements Renderer {
renderCircle(r: number): void { console.log(`Drawing circle (r=${r}) as vector`); }
renderSquare(s: number): void { console.log(`Drawing square (s=${s}) as vector`); }
}
class RasterRenderer implements Renderer {
renderCircle(r: number): void { console.log(`Drawing circle (r=${r}) as pixels`); }
renderSquare(s: number): void { console.log(`Drawing square (s=${s}) as pixels`); }
}3Step 3: Define the abstraction hierarchy
abstract class Shape {
constructor(protected renderer: Renderer) {}
abstract draw(): void;
abstract resize(factor: number): void;
}
class Circle extends Shape {
constructor(renderer: Renderer, private radius: number) { super(renderer); }
draw(): void { this.renderer.renderCircle(this.radius); }
resize(factor: number): void { this.radius *= factor; }
}
class Square extends Shape {
constructor(renderer: Renderer, private side: number) { super(renderer); }
draw(): void { this.renderer.renderSquare(this.side); }
resize(factor: number): void { this.side *= factor; }
}4Step 4: Mix and match abstractions with implementations
const vector = new VectorRenderer();
const raster = new RasterRenderer();
new Circle(vector, 5).draw(); // Drawing circle (r=5) as vector
new Square(raster, 10).draw(); // Drawing square (s=10) as pixels// Bridge Pattern – Production
// Notification system: the abstraction (Notification) is bridged to
// independently extensible channel implementations (Email, SMS, Push).
// ── Implementation side: channels ─────────────────────────────────────────────
export interface NotificationChannel {
send(recipient: string, subject: string, body: string): Promise<void>;
}
export class EmailChannel implements NotificationChannel {
async send(to: string, subject: string, body: string): Promise<void> {
console.log(`[Email → ${to}] Subject: ${subject}\n${body}`);
}
}
export class SmsChannel implements NotificationChannel {
async send(to: string, _subject: string, body: string): Promise<void> {
const truncated = body.length > 160 ? body.slice(0, 157) + "..." : body;
console.log(`[SMS → ${to}] ${truncated}`);
}
}
export class PushChannel implements NotificationChannel {
async send(to: string, subject: string, body: string): Promise<void> {
console.log(`[Push → ${to}] ${subject}: ${body.slice(0, 80)}`);
}
}
// ── Abstraction side: notification types ──────────────────────────────────────
export abstract class Notification {
constructor(protected channel: NotificationChannel) {}
abstract notify(recipient: string, data: Record<string, string>): Promise<void>;
}
export class AlertNotification extends Notification {
async notify(recipient: string, data: Record<string, string>): Promise<void> {
const subject = `⚠️ Alert: ${data.event ?? "Unknown event"}`;
const body = `An alert was triggered at ${data.timestamp ?? new Date().toISOString()}.\nDetails: ${data.details ?? "n/a"}`;
await this.channel.send(recipient, subject, body);
}
}
export class WelcomeNotification extends Notification {
async notify(recipient: string, data: Record<string, string>): Promise<void> {
const subject = "Welcome to the platform!";
const body = `Hi ${data.name ?? "there"}, your account is ready. Enjoy!`;
await this.channel.send(recipient, subject, body);
}
}
// ── Usage ─────────────────────────────────────────────────────────────────────
(async () => {
// Same alert, different channels – zero code duplication
const emailAlert = new AlertNotification(new EmailChannel());
const smsAlert = new AlertNotification(new SmsChannel());
const pushWelcome = new WelcomeNotification(new PushChannel());
await emailAlert.notify("[email protected]", {
event: "CPU spike",
timestamp: "2026-03-11T14:00:00Z",
details: "CPU usage exceeded 95% for 5 minutes",
});
await smsAlert.notify("+14155550100", {
event: "CPU spike",
timestamp: "2026-03-11T14:00:00Z",
details: "CPU usage exceeded 95% for 5 minutes",
});
await pushWelcome.notify("user-987", { name: "Alice" });
})();Bridge Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Bridge Pattern in the Real World
“A TV remote (abstraction) works with any brand of television (implementation) because both sides communicate through an agreed IR protocol (the bridge). Samsung and LG can redesign their TVs, and universal remote makers can add new button layouts—neither side needs to know about the other’s internal design, only the shared protocol.”