Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Access Modifiers in TypeScript

Access Modifiers

~14 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

So far, ALL properties of our classes were readable and modifiable from ANYWHERE in the code. Access modifiers let you protect a class's internal state from uncontrolled outside access – a core object-oriented programming principle called "encapsulation".

public, private, protected

export class Library {
  private stock: Book[]; // readable/writable ONLY inside this class
  public name: string;   // accessible from ANYWHERE (the default when nothing is specified)

  constructor(name: string, initialStock: Book[]) {
    this.name = name;
    this.stock = initialStock;
  }

  public bookCount(): number {
    return this.stock.length; // ✓ allowed - inside the class
  }
}

const library = new Library('City Library', []);
library.name;              // ✓ allowed - public
library.stock;             // ✗ error - private, not accessible from outside
library.bookCount();       // ✓ allowed - public method

public is the DEFAULT – every property/method WITHOUT an explicit modifier is automatically public. private is the exact opposite: ONLY code INSIDE the same class may access it, not even derived classes.

protected: visible to derived classes

export class Medium {
  protected renewals: number = 0; // visible to Medium AND all derived classes

  protected checkAvailability(): boolean {
    return this.status === 'available';
  }
}

export class Book extends Medium {
  checkOutWithRenewal(): void {
    if (this.checkAvailability()) {  // ✓ allowed - protected, Book INHERITS from Medium
      this.renewals++;                // ✓ allowed
    }
  }
}

const book = new Book(/* ... */);
book.renewals; // ✗ error - protected, NOT accessible from outside the class hierarchy

protected is the "middle ground" between public and private: visible to the class ITSELF and ALL classes that INHERIT from it, but NOT to code outside this hierarchy.

In practice: privatizing stock in Library

Remember Library from chapter 12? stock was public – ANY code could manipulate the array directly (library.stock.push(...)), WITHOUT going through the controlled checkOut methods. Let's protect it now:

src/services/Library.ts
import { Book } from '../models/Book.js';

export class Library {
  private stock: Book[];

  constructor(initialStock: Book[]) {
    this.stock = initialStock;
  }

  public bookCount(): number {
    return this.stock.length;
  }

  public findByTitle(title: string): Book | undefined {
    return this.stock.find((book) => book.title === title);
  }

  public checkOut(isbn: string): boolean {
    const book = this.stock.find((b) => b.isbn === isbn);
    if (!book || book.status !== 'available') {
      return false;
    }
    book.status = 'borrowed';
    return true;
  }

  public returnBook(isbn: string): boolean {
    const book = this.stock.find((b) => b.isbn === isbn);
    if (!book || book.status !== 'borrowed') {
      return false;
    }
    book.status = 'available';
    return true;
  }

  public allBooks(): readonly Book[] {
    return this.stock; // returns a READABLE view, no direct write access
  }
}

allBooks(): readonly Book[] combines chapter 11 (readonly arrays) with this chapter: from outside, the stock can be READ (e.g. iterated with .map(), .filter()), but NOT modified directly – every change MUST go through checkOut/returnBook, which manage the status correctly and in a controlled way.

Achtung: Access modifiers are a PURE compile-time check – unlike languages like Java, private/protected disappear ENTIRELY when compiling to JavaScript (see chapter 1: type annotations only exist at development time). At runtime, library.stock is technically still reachable via JavaScript-native tricks – for GENUINE runtime encapsulation, there are private class fields with a # prefix (a native JavaScript feature, not TypeScript-specific), not covered further here for the sake of clarity.

Tipp: Rule of thumb: make properties private by default, only relaxing to public/protected when there's an ACTUAL need from outside (or for derived classes). This "restrictive first" mindset prevents internal state from accidentally becoming depended on by code you can no longer safely change later.