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
| Modifier | Visible from |
|---|---|
public (default) | Anywhere |
protected | The class itself and subclasses |
private | Only the declaring class itself, not even subclasses |
readonly | Not 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).