TypeScript classes add access modifiers on top of JavaScript's class syntax: public (the default, accessible anywhere), private (only within the declaring class), and protected (the declaring class and its subclasses). readonly prevents reassignment after construction. implements checks that a class satisfies an interface's shape at compile time. All of these are TypeScript-only checks — they're stripped away entirely when compiled to JavaScript, so private in particular offers no real runtime protection, unlike JavaScript's native #-prefixed private fields.
Access modifiers: public, private, protected
public is the default and rarely written explicitly; private and protected are checked only by the TypeScript compiler, not enforced by the JavaScript runtime that actually executes.
class BankAccount {
public owner: string;
private balance: number;
constructor(owner: string, balance: number) {
this.owner = owner;
this.balance = balance;
}
public deposit(amount: number) {
this.balance += amount;
}
}
const acc = new BankAccount("Alice", 100);
acc.deposit(50); // fine — deposit is public
// acc.balance; // COMPILE ERROR: balance is privatereadonly properties
readonly blocks reassignment after the constructor runs, but only at the TypeScript-checking layer — it's a compile-time guarantee, not a runtime freeze like Object.freeze.
class Point {
readonly x: number;
readonly y: number;
constructor(x: number, y: number) {
this.x = x; // fine — inside the constructor
this.y = y;
}
}
const p = new Point(3, 4);
// p.x = 10; // COMPILE ERROR: x is readonlyimplements: enforcing an interface's shape
implements is purely a compile-time check that the class satisfies the interface — it doesn't affect runtime behavior at all, and a class can implement multiple interfaces at once.
interface Flyable {
fly(): void;
}
class Bird implements Flyable {
fly() {
console.log("Flying!");
}
}
// class Rock implements Flyable {} // COMPILE ERROR: missing fly() method