TypeScript Interfaces and Classes
/ 4 min read
Interfaces
1. Basic Interface Declaration
// Simple interfaceinterface User { id: number; name: string; email: string; age?: number; // Optional property readonly createdAt: Date; // Read-only property}
// Implementationconst user: User = { id: 1, name: "John Doe", email: "john@example.com", createdAt: new Date()};2. Interface Extension
// Base interfaceinterface Animal { name: string; age: number;}
// Extended interfaceinterface Pet extends Animal { owner: string; breed?: string;}
// Multiple interface extensioninterface ServiceDog extends Pet { training: string[]; certification: string;}3. Function Interfaces
// Function interfaceinterface MathOperation { (x: number, y: number): number;}
// Implementationconst add: MathOperation = (x, y) => x + y;const subtract: MathOperation = (x, y) => x - y;
// Method interfaceinterface Calculator { add(x: number, y: number): number; subtract(x: number, y: number): number;}Classes
1. Basic Class Declaration
class Person { // Properties private name: string; protected age: number; readonly id: number;
// Constructor constructor(name: string, age: number) { this.name = name; this.age = age; this.id = Math.random(); }
// Methods public getName(): string { return this.name; }
public setName(name: string): void { this.name = name; }}2. Class Implementation of Interfaces
interface Vehicle { start(): void; stop(): void; speed: number;}
class Car implements Vehicle { speed: number = 0;
start(): void { console.log("Car started"); }
stop(): void { console.log("Car stopped"); this.speed = 0; }
accelerate(increment: number): void { this.speed += increment; }}3. Class Inheritance
// Base classclass Animal { protected name: string;
constructor(name: string) { this.name = name; }
makeSound(): void { console.log("Some sound"); }}
// Derived classclass Dog extends Animal { private breed: string;
constructor(name: string, breed: string) { super(name); this.breed = breed; }
makeSound(): void { console.log("Woof!"); }
getInfo(): string { return `${this.name} is a ${this.breed}`; }}Advanced Class Features
1. Abstract Classes
abstract class Shape { abstract getArea(): number; abstract getPerimeter(): number;
// Common method describe(): string { return `Area: ${this.getArea()}, Perimeter: ${this.getPerimeter()}`; }}
class Rectangle extends Shape { constructor(private width: number, private height: number) { super(); }
getArea(): number { return this.width * this.height; }
getPerimeter(): number { return 2 * (this.width + this.height); }}2. Static Members
class MathUtils { static PI: number = 3.14159;
static square(x: number): number { return x * x; }
static get random(): number { return Math.random(); }}
// Usageconsole.log(MathUtils.PI);console.log(MathUtils.square(5));console.log(MathUtils.random);3. Accessors
class Employee { private _salary: number;
constructor(private name: string, salary: number) { this._salary = salary; }
// Getter get salary(): number { return this._salary; }
// Setter set salary(value: number) { if (value < 0) { throw new Error("Salary cannot be negative"); } this._salary = value; }}Advanced Interface Features
1. Index Signatures
interface StringMap { [key: string]: string;}
interface NumberMap { [index: number]: string;}
// Implementationconst stringMap: StringMap = { name: "John", email: "john@example.com"};
const numberMap: NumberMap = ["first", "second", "third"];2. Hybrid Types
interface Counter { (start: number): string; interval: number; reset(): void;}
function getCounter(): Counter { const counter = function(start: number) { return String(start); } as Counter;
counter.interval = 123; counter.reset = function() {};
return counter;}3. Declaration Merging
interface Box { height: number; width: number;}
interface Box { scale: number;}
// Results in:// interface Box {// height: number;// width: number;// scale: number;// }
const box: Box = { height: 5, width: 6, scale: 10 };Design Patterns with Classes
1. Singleton Pattern
class Singleton { private static instance: Singleton; private constructor() {}
public static getInstance(): Singleton { if (!Singleton.instance) { Singleton.instance = new Singleton(); } return Singleton.instance; }}2. Factory Pattern
interface Product { name: string; price: number;}
class ProductFactory { static createProduct(type: string): Product { switch (type) { case "book": return { name: "Book", price: 10 }; case "electronics": return { name: "Electronics", price: 100 }; default: throw new Error("Invalid product type"); } }}Best Practices
- Use interfaces for object type definitions
- Keep classes focused and single-responsibility
- Prefer composition over inheritance
- Use access modifiers appropriately
- Implement interfaces for better type checking
- Use abstract classes for common functionality
- Document complex interfaces and classes
Common Patterns
1. Builder Pattern
class UserBuilder { private user: any = {};
setName(name: string): UserBuilder { this.user.name = name; return this; }
setAge(age: number): UserBuilder { this.user.age = age; return this; }
build(): User { return this.user as User; }}2. Decorator Pattern
interface Coffee { cost: number; description: string;}
class SimpleCoffee implements Coffee { cost = 10; description = "Simple coffee";}
class MilkDecorator implements Coffee { constructor(private coffee: Coffee) {}
get cost(): number { return this.coffee.cost + 2; }
get description(): string { return this.coffee.description + ", milk"; }}Conclusion
Understanding interfaces and classes in TypeScript is crucial for building well-structured, maintainable applications. These concepts provide the foundation for object-oriented programming in TypeScript.
Series Navigation
- Previous: TypeScript Functions and Methods
- Next: TypeScript Generics