Access Modifiers

`public`, `private`, `protected` — and `#` for Real Privacy

Access Modifiers

TS adds access modifiers to control which members are reachable from outside the class. Plus JS's `#` for actual runtime privacy.

4 min read Level 1/5 #typescript#classes#access-modifiers
What you'll learn
  • Use `public` / `private` / `protected`
  • Know that TS `private` is a compile-time check
  • Use `#field` for runtime-private state

By default, every class member is public — reachable from anywhere. TS adds modifiers to restrict access.

public (Default)

class User {
  public name: string;     // same as: name: string
  constructor(name: string) { this.name = name; }
}

You rarely write public — it’s the default.

private — Same Class Only

class Counter {
  private count = 0;
  increment() { this.count++; }
  value() { return this.count; }
}

const c = new Counter();
c.increment();
c.value();        // ✓
c.count;          // ✗ Property 'count' is private

private is a compile-time check. Nothing stops a JS caller from poking at c.count at runtime — it’s erased.

protected — Subclass-Accessible

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

class Dog extends Animal {
  bark() { return `${this.name} says woof`; }   // ✓ subclass can read
}

const d = new Dog("Rex");
d.name;   // ✗ protected — not reachable outside the class hierarchy

Like private, but subclasses also get access.

readonly Combo

class User {
  constructor(public readonly id: string) {}
}

Public — but can’t be reassigned after construction.

#field — Real Privacy (JS Feature)

ECMAScript shipped #field for true runtime privacy. TS supports it:

class Counter {
  #count = 0;
  increment() { this.#count++; }
  value() { return this.#count; }
}

const c = new Counter();
c.increment();
c.value();        // ✓
c.#count;         // ✗ SyntaxError — and undetectable at runtime

Difference from private:

ModifierCompile checkRuntime check
privateYesNo (erased)
#fieldYesYes (real privacy)

For new code: prefer #field. It’s a JS feature, not a TS-only thing, and it actually hides the state. Use private when you need to interop with code that expects field names without # — or with older targets.

Up Next

Abstract classes — half-implemented base classes that force subclasses to fill in the gaps.

Abstract Classes →