Implementing Interfaces
Implementing Interfaces
Classes can implement one or more interfaces, enforcing a contract.
Basic implements
interface Serializable {
serialize(): string;
deserialize(data: string): void;
}
class User implements Serializable {
constructor(
public name: string,
public email: string
) {}
serialize(): string {
return JSON.stringify({ name: this.name, email: this.email });
}
deserialize(data: string): void {
const obj = JSON.parse(data);
this.name = obj.name;
this.email = obj.email;
}
}
Multiple Interface Implementation
interface Printable {
print(): void;
}
interface Loggable {
log(): void;
}
class Document implements Printable, Loggable {
constructor(public title: string, public content: string) {}
print(): void {
console.log(`${this.title}: ${this.content}`);
}
log(): void {
console.log(`[${new Date().toISOString()}] ${this.title}`);
}
}
Interface as Class Contracts
Interface as Class Contracts
Repository Pattern
interface Repository<T> {
findById(id: string): Promise<T | null>;
findAll(): Promise<T[]>;
create(entity: Omit<T, 'id'>): Promise<T>;
update(id: string, data: Partial<T>): Promise<T | null>;
delete(id: string): Promise<boolean>;
}
interface User {
id: string;
name: string;
email: string;
}
class UserRepository implements Repository<User> {
private users: Map<string, User> = new Map();
async findById(id: string): Promise<User | null> {
return this.users.get(id) ?? null;
}
async findAll(): Promise<User[]> {
return Array.from(this.users.values());
}
async create(data: Omit<User, 'id'>): Promise<User> {
const user: User = { ...data, id: crypto.randomUUID() };
this.users.set(user.id, user);
return user;
}
async update(id: string, data: Partial<User>): Promise<User | null> {
const user = this.users.get(id);
if (!user) return null;
const updated = { ...user, ...data };
this.users.set(id, updated);
return updated;
}
async delete(id: string): Promise<boolean> {
return this.users.delete(id);
}
}
Dependency Injection
Dependency Injection with Interfaces
Interface-Based DI
interface Logger {
info(message: string): void;
error(message: string, error?: Error): void;
warn(message: string): void;
}
interface EmailService {
send(to: string, subject: string, body: string): Promise<boolean>;
}
class ConsoleLogger implements Logger {
info(message: string): void {
console.log(`[INFO] ${message}`);
}
error(message: string, error?: Error): void {
console.error(`[ERROR] ${message}`, error);
}
warn(message: string): void {
console.warn(`[WARN] ${message}`);
}
}
class NotificationService {
constructor(
private logger: Logger,
private emailService: EmailService
) {}
async notifyUser(email: string, message: string): Promise<boolean> {
this.logger.info(`Sending notification to ${email}`);
const sent = await this.emailService.send(email, 'Notification', message);
if (sent) {
this.logger.info('Notification sent successfully');
} else {
this.logger.error('Failed to send notification');
}
return sent;
}
}
// Inject concrete implementations
const logger = new ConsoleLogger();
const emailService = new SmtpEmailService();
const notificationService = new NotificationService(logger, emailService);
Structural Compatibility
Structural Compatibility
Class Structural Typing
interface Point {
x: number;
y: number;
}
class MyPoint {
constructor(public x: number, public y: number) {}
}
// MyPoint is structurally compatible with Point
function logPoint(p: Point): void {
console.log(`${p.x}, ${p.y}`);
}
const mp = new MyPoint(10, 20);
logPoint(mp); // OK - structural compatibility
Private Members and Compatibility
class Animal {
private name: string;
constructor(name: string) {
this.name = name;
}
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name);
}
}
// Dog is compatible with Animal (structural)
function getName(animal: Animal): string {
return animal.name; // Error: private
}
Best Practices
- Program to interfaces, not implementations
- Use interfaces for all public API boundaries
- Keep interfaces small and focused
- Use interface extension for composing contracts
- Favor interfaces over abstract classes when possible