Hint
public = accessible everywhere; private = class only; protected = class + subclasses; readonly = no reassignment after init
TypeScript adds access modifiers to class members to enforce encapsulation (compile-time only — no runtime enforcement).
class BankAccount {
readonly id: string; // set once in constructor, never changed
public owner: string; // accessible everywhere (default)
protected balance: number; // accessible in this class and subclasses
private #internalRef: string; // true private (JS private field — runtime enforced)
private log(msg: string) { // TS private — compile-time only
console.log(msg);
}
constructor(owner: string) {
this.id = crypto.randomUUID();
this.owner = owner;
this.balance = 0;
this.#internalRef = 'ref';
}
deposit(amount: number) {
this.balance += amount; // ✅ accessible from own class
this.log('deposited'); // ✅ private method ok inside class
}
}
class SavingsAccount extends BankAccount {
interestRate = 0.05;
addInterest() {
this.balance *= (1 + this.interestRate); // ✅ protected accessible in subclass
}
}
const acct = new BankAccount('Alice');
acct.owner; // ✅ public
acct.balance; // ❌ protected — only inside class hierarchy
acct.log(''); // ❌ private
Constructor shorthand — declare and initialize in one line:
class Point {
constructor(
public readonly x: number,
public readonly y: number
) {}
// Equivalent to: this.x = x; this.y = y; declared as readonly public
}
private is erased at runtime. Use JavaScript's native #privateField syntax for true runtime privacy. readonly can be bypassed at runtime but is a strong signal to consumers.