Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Mastering Classes and Interfaces in TypeScript: From Fundamentals to Advanced Patterns

Tech 1

Core Concepts

TypeScript leverages classes and interfaces to enforce type safety while supporting object-oriented design. Classes provide concrete implementations, whereas interfaces define structural contracts without implementation.

class Employee {
  name: string;
  department: string;

  constructor(name: string, dept: string) {
    this.name = name;
    this.department = dept;
  }

  introduce() {
    console.log(`Hi, I'm ${this.name} from ${this.department}.`);
  }
}

const emp = new Employee("Sarah", "Engineering");
emp.introduce(); // Hi, I'm Sarah from Engineering.

Interfaces specify the shape an object must conform to:

interface Drawable {
  draw(): void;
}

class Circle implements Drawable {
  radius: number;

  constructor(radius: number) {
    this.radius = radius;
  }

  draw() {
    console.log(`Drawing circle with radius ${this.radius}`);
  }
}

const shape: Drawable = new Circle(5);
shape.draw(); // Drawing circle with radius 5

Key Differences

  • Instantiation: Classes can be instantiated with new; interfaces cannot.
  • Implementation: Classes contain logic; interfaces are purely declarative.
  • Inheritance: A class extends one other class but can implement multiple interfaces.
  • Static Members: Only classes support static properties and methods.

Advanced Interface Features

Optional and Readonly Properties

interface Config {
  host: string;
  port?: number;           // optional
  readonly apiKey: string; // immutable after creation
}

const cfg: Config = { host: "localhost", apiKey: "abc123" };
cfg.port = 8080; // OK
// cfg.apiKey = "xyz"; // Error: readonly

Function Type Interfaces

interface BinaryOperation {
  (x: number, y: number): number;
}

const multiply: BinaryOperation = (a, b) => a * b;

Index Signatures

interface Dictionary {
  [key: string]: any;
}

const metadata: Dictionary = { version: "1.0", author: "dev" };

Interface Inheritance

interface Vehicle {
  wheels: number;
}

interface Car extends Vehicle {
  doors: number;
}

const sedan: Car = { wheels: 4, doors: 4 };

Advanced Class Features

Access Modifiers

class BankAccount {
  public balance: number;
  private pin: string;
  protected owner: string;

  constructor(balance: number, pin: string, owner: string) {
    this.balance = balance;
    this.pin = pin;
    this.owner = owner;
  }
}

const account = new BankAccount(1000, "1234", "Alex");
console.log(account.balance); // OK
// console.log(account.pin); // Error: private

Static Members

class Constants {
  static GRAVITY = 9.81;
  static computeWeight(mass: number) {
    return mass * this.GRAVITY;
  }
}

console.log(Constants.computeWeight(70)); // ~686.7

Abstract Classes

abstract class Component {
  abstract render(): void;

  attach() {
    console.log("Component attached");
  }
}

class Button extends Component {
  render() {
    console.log("<button>Click me</button>");
  }
}

const btn = new Button();
btn.render(); // <button>Click me</button>
btn.attach(); // Component attached

Combining Classes and Interfaces

Implementing Multiple Interfaces

interface Authenticatable {
  authenticate(token: string): boolean;
}

interface Cacheable {
  cacheKey(): string;
}

class ApiClient implements Authenticatable, Cacheable {
  authenticate(token: string) {
    return token.length > 10;
  }

  cacheKey() {
    return "api-client-v1";
  }
}

Interfaces Extending Classes

class UIControl {
  private enabled = true;
}

interface ClickableControl extends UIControl {
  onClick(): void;
}

class Checkbox extends UIControl implements ClickableControl {
  onClick() {
    console.log("Checkbox toggled");
  }
}

Constructor Signatures via Interfaces

interface TimerConstructor {
  new (duration: number): TimerInterface;
}

interface TimerInterface {
  start(): void;
}

function createTimer(
  ctor: TimerConstructor,
  duration: number
): TimerInterface {
  return new ctor(duration);
}

class CountdownTimer implements TimerInterface {
  constructor(private seconds: number) {}
  start() {
    console.log(`Starting countdown: ${this.seconds}s`);
  }
}

const timer = createTimer(CountdownTimer, 30);
timer.start(); // Starting countdown: 30s

When to Use Classes vs Interfaces

Use classes when:

  • You need to instantiate objects
  • Behavior and state must be encapsulated together
  • Inheritance or polymorphism is required
  • Fine-grained access control (private, protected) is needed

Use interfaces when:

  • Defining APIs or contracts between modules
  • Enabling loose coupling
  • Validating object structure without providing logic
  • Combining multiple unrelated capabilities into one type

Best Practices

  • Prefer interfaces for type definitions to promote flexibility.
  • Use abstract classes when sharing implementation across related types.
  • Keep interfaces focused on a single responsibility.
  • Avoid premature abstraction—introduce classes or interfaces only when they solve a clear design problem.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.