Modifiers, Public, Protected, Private in Typescript
Access modifiers control who can see and use a class member. TypeScript gives you three: public, protected, and private. Each draws a different boundary.
15 Apr 2024

Access modifiers control who can see and use a class member. TypeScript gives you three: public, protected, and private. Each draws a different boundary.
The three levels
Public — accessible from anywhere. This is the default. If you don't specify a modifier, it's public.
Protected — accessible inside the class and its subclasses. Not accessible from outside code.
Private — accessible only inside the class that defines it. Not even subclasses can touch it.
class Person {
public name: string;
protected age: number;
private email: string;
constructor(name: string, age: number, email: string) {
this.name = name;
this.age = age;
this.email = email;
}
greet() {
console.log(`Hello, my name is ${this.name}.`);
}
}
What happens in a subclass
A subclass can access public and protected members. private members are off limits.
class Employee extends Person {
constructor(name: string, age: number, email: string) {
super(name, age, email);
}
showDetails() {
console.log(`Name: ${this.name}, Age: ${this.age}`); // works
console.log(`Email: ${this.email}`); // Error: 'email' is private
}
}
What happens from outside
const person = new Person('Alice', 30, 'alice@example.com');
console.log(person.name); // works
console.log(person.age); // Error: 'age' is protected
console.log(person.email); // Error: 'email' is private
The trade-off
Access modifiers enforce encapsulation at compile time. They prevent accidental coupling to internal state. That's the benefit.
The cost: this is compile-time only. At runtime, JavaScript has no concept of protected. Anyone with access to the object can still reach any property. TypeScript's private is a guardrail, not a wall.
If you need true runtime privacy, use JavaScript's # private fields instead. But for most codebases, TypeScript's modifiers are enough to keep the architecture clean.
Keep reading
- The Frontend Toolchain Is Now Written in Rust and Go. What That Means for You
- oxlint vs ESLint: Why I Replaced ESLint with oxlint
- SystemJS Is Dead. Native ESM Finally Won.
- Replace Axios with a Simple Custom Fetch Wrapper (Production-Ready Guide)
- Stop Shipping ChatGPT Wrappers. Ship an Agent in TypeScript, or Don't Bother.
- What Developers Are Saying About React 19: Pros and Cons