TypeScript Classes

TypeScript builds on JavaScript classes with access modifiers, readonly properties, and interfaces so you can enforce object shapes at compile time.

Access Modifiers

JavaScript classes have no built-in concept of private state that the compiler enforces (aside from the newer # syntax). TypeScript adds public, private, and protected keywords that control which code is allowed to read or write a property, catching accidental external access before your code runs.

public, private, and protected

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

  constructor(owner: string, balance: number) {
    this.owner = owner;
    this.balance = balance;
    this.accountType = "standard";
  }

  public deposit(amount: number): void {
    this.balance += amount;
  }

  public getBalance(): number {
    return this.balance;
  }
}

const acc = new BankAccount("Ravi", 100);
acc.deposit(50);
console.log(acc.getBalance());
// acc.balance; // Error: 'balance' is private
  • public — accessible from anywhere; this is the default if you omit a modifier.
  • private — accessible only inside the declaring class, not subclasses or outside code.
  • protected — accessible inside the declaring class and any class that extends it.
  • readonly — can be set once in the constructor and never reassigned afterward.
Note: Constructor parameter properties let you declare and assign a property in one step: 'constructor(private balance: number) {}' is equivalent to declaring and assigning it manually.

Readonly Properties

The readonly modifier protects a property from being reassigned after construction, which is useful for values that should stay fixed for the lifetime of an object, like an id or a creation timestamp.

Using readonly

class Invoice {
  readonly id: string;
  readonly createdAt: Date;
  amount: number;

  constructor(id: string, amount: number) {
    this.id = id;
    this.createdAt = new Date();
    this.amount = amount;
  }
}

const inv = new Invoice("INV-001", 250);
inv.amount = 300; // fine
// inv.id = "INV-002"; // Error: id is readonly

Implementing Interfaces

An interface describes the shape a class must have without providing any implementation. When a class uses implements, the compiler checks that every member listed in the interface is actually present with a compatible type, which is a great way to enforce a contract across multiple classes.

implements Keyword

interface Shape {
  area(): number;
  perimeter(): number;
}

class Rectangle implements Shape {
  constructor(private width: number, private height: number) {}

  area(): number {
    return this.width * this.height;
  }

  perimeter(): number {
    return 2 * (this.width + this.height);
  }
}

const rect = new Rectangle(4, 5);
console.log(rect.area(), rect.perimeter());
ModifierVisible outside class?Visible in subclass?
publicYesYes
protectedNoYes
privateNoNo
readonlyDepends on public/privateDepends on public/private
Note: A class can implement multiple interfaces at once: 'class Employee implements Payable, Schedulable { ... }'.

Exercise: TypeScript Classes

What does marking a class field as `private` do?