How to Use Access Modifiers in TypeScript

Use access modifiers in TypeScript when class properties or methods need clear visibility rules. They help protect internal state, expose safe public APIs, and support inheritance-friendly design.

What you’ll build or solve

You’ll learn how to use public, private, and protected in TypeScript classes. You’ll also know when each modifier creates safer class design.

When this approach works best

This approach is the right choice when class internals should not all be directly accessible.

Common real-world scenarios include:

  • Bank balances
  • API client tokens
  • UI controller state
  • Base class helpers
  • Domain model rules

This is a bad idea when the class is only a temporary learning example with no real encapsulation needs.

Prerequisites

You only need:

  • Basic TypeScript classes
  • Familiarity with constructors
  • Understanding of inheritance basics

Step-by-step instructions

Step 1: Use public for the external API

public is the default visibility.

class User {
  public name: string;

  constructor(name: string) {
    this.name = name;
  }
}

This property is available anywhere.

const user = new User("Alex");
console.log(user.name);

Use this for values intentionally exposed.

Step 2: Use private for internal-only state

Private members are accessible only inside the class.

class BankAccount {
  private balance: number;

  constructor(balance: number) {
    this.balance = balance;
  }

  getBalance(): number {
    return this.balance;
  }
}

This prevents direct external mutation.

Step 3: Use protected for inheritance

Protected members are available in subclasses.

class Animal {
  protected name: string;

  constructor(name: string) {
    this.name = name;
  }
}

class Dog extends Animal {
  bark(): string {
    return `${this.name} is barking`;
  }
}

This is ideal for base-class shared logic.

What to look for:

  • public exposes the API
  • private hides internals
  • protected supports subclasses
  • Great for safer class boundaries
  • Improves maintainability

Examples you can copy

Public product name

public name: string

Private API token

private token: string

Protected base helper

protected formatName(): string

Common mistakes and how to fix them

Mistake 1: Making everything public

What the reader might do:

Expose sensitive internal fields.

Why it breaks: any external code can mutate internals.

Corrected approach:

Use private for protected state.

Mistake 2: Using private when subclasses need access

What the reader might do:

private name: string

in a base class used by child classes.

Why it breaks: subclasses cannot read it.

Corrected approach:

Use protected.

Mistake 3: Hiding values that should be part of the API

What the reader might do:

Mark externally needed properties as private.

Why it breaks: the class becomes harder to use.

Corrected approach:

Expose a public getter or make it public.

Troubleshooting

If a subclass cannot access a property, switch from private to protected.

If external code changes internal state, make the field private.

If the API feels awkward, expose controlled getters.

If encapsulation is unnecessary, simpler visibility may be enough.

Quick recap

  • public exposes values
  • private hides internal state
  • protected supports subclasses
  • Use visibility intentionally
  • Prefer getters for controlled access