CodeOath
← All posts
TypeScript75 min total · 21 parts

TypeScript Fundamentals: Types, Interfaces, Generics, and Why It Catches Bugs Before Runtime

Contents — Part 16 of 21: Classes: Access Modifiers, Readonly, and Abstract
Part 16 of 21 · ~2 min

Classes: Access Modifiers, Readonly, and Abstract

TypeScript adds access modifiers and a few conveniences on top of JavaScript's native classes:

class BankAccount {
  private balance: number;
  readonly accountId: string;

  constructor(accountId: string, initialBalance: number) {
    this.accountId = accountId;
    this.balance = initialBalance;
  }

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

  getBalance(): number {
    return this.balance; // fine — accessed from inside the class
  }
}

const account = new BankAccount("acc-1", 100);
account.balance;      // Error: Property 'balance' is private
account.accountId = "x"; // Error: Cannot assign to 'accountId' because it is a read-only property
ModifierVisible from
public (default)Anywhere
protectedThe class itself and subclasses
privateOnly the declaring class itself, not even subclasses
readonlyNot a visibility modifier — assignable only in the declaration or constructor

A common shorthand collapses constructor parameters and field declarations into one:

class BankAccount2 {
  constructor(
    public readonly accountId: string,
    private balance: number,
  ) {}
}
// equivalent to declaring both fields above and assigning them in the constructor body

abstract class defines a base class that can't be instantiated directly and can declare method signatures subclasses must implement:

abstract class Shape {
  abstract area(): number; // no body — subclasses must provide one
  describe(): string {
    return `Area: ${this.area()}`;
  }
}

class Circle extends Shape {
  constructor(private radius: number) { super(); }
  area(): number { return Math.PI * this.radius ** 2; }
}

new Shape(); // Error: Cannot create an instance of an abstract class

Note that TypeScript's access modifiers are a compile-time-only concept, same as everything else covered so far — private fields declared with the private keyword are still ordinary, inspectable properties on the compiled JavaScript object at runtime. True runtime privacy needs JavaScript's own #privateField syntax instead, which TypeScript also supports and actually enforces at runtime (a #-prefixed field is genuinely inaccessible from outside the class, even via Object.keys or bracket access).