DevAcademy
LearnTypeScriptAccess Modifiers
IntermediateTypeScript

Access Modifiers

Learn how public, private, protected, and readonly control the visibility of class members.

Reading Time

14 min

Lesson

Lesson 17 of 30

What Are Access Modifiers?

Access modifiers control where a class member can be accessed from — the class itself, its subclasses, or anywhere. TypeScript enforces them at compile time.

Access Modifiers

ModifierAccessible From
public (default)Anywhere — the class, subclasses, and outside code
privateOnly within the declaring class itself
protectedThe declaring class and its subclasses, but not outside code
readonlyCan be combined with any of the above; prevents reassignment after construction

public, private, and protected

class BankAccount {
  public accountHolder: string;
  private balance: number;
  protected accountNumber: string;

  constructor(accountHolder: string, balance: number, accountNumber: string) {
    this.accountHolder = accountHolder;
    this.balance = balance;
    this.accountNumber = accountNumber;
  }

  deposit(amount: number): void {
    this.balance += amount; // OK: inside the class
  }
}

const account = new BankAccount("Alice", 1000, "ACC-001");
account.balance; // Error: 'balance' is private

protected in Subclasses

A protected member is hidden from outside code but remains accessible to any class that extends the declaring class, useful for internal details that subclasses still need.

protected Accessible in a Subclass

class SavingsAccount extends BankAccount {
  showAccountNumber(): string {
    return this.accountNumber; // OK: protected, accessible in a subclass
  }
}

Native JavaScript Private Fields

JavaScript itself now supports true private fields with a # prefix, which are enforced at runtime, not just compile time — unlike TypeScript’s private keyword, which is erased when compiled.

Native # Private Fields

class Counter {
  #count = 0;

  increment(): void {
    this.#count++;
  }

  get value(): number {
    return this.#count;
  }
}

const counter = new Counter();
counter.#count; // Error, and also fails at runtime — truly inaccessible

TypeScript private is Compile-Time Only

Because TypeScript’s private is erased during compilation, the compiled JavaScript output has no real enforcement — code that bypasses the type checker (or plain JavaScript consumers) can still access it at runtime.

Best Practice

Reach for native # private fields when you need genuine runtime privacy (like a library’s internal state), and TypeScript’s private/protected for everyday encapsulation within your own typed codebase.

Interview Questions

Quick Quiz

1. Which access modifier is the default when none is specified?

2. Can a protected member be accessed from a subclass?

3. What is the key difference between TypeScript’s private and native # private fields?