- TypeScript Tutorial
- TypeScript Home
- TypeScript Introduction
- TypeScript Setup
- TypeScript First Program
- TypeScript vs JavaScript
- TypeScript Data Types
- TypeScript Type Inference
- TypeScript Type Annotations
- TypeScript Interfaces
- TypeScript Enums
- TypeScript Type Aliases
- TypeScript Type Assertions
- TypeScript Variables
- TypeScript Functions
- TypeScript Functions
- TypeScript Optional Parameters
- TypeScript Default Parameters
- TypeScript Rest Parameters
- TypeScript Arrow Functions
- Classes and Objects
- Introduction to Classes
- Properties and Methods
- Access Modifiers
- Static Members
- Inheritance
- Abstract Classes
- Interfaces vs Classes
- Advanced Types
- TypeScript Union Types
- TypeScript Intersection Types
- TypeScript Literal Types
- TypeScript Nullable Types
- TypeScript Type Guards
- TypeScript Discriminated Unions
- TypeScript Index Signatures
- TypeScript Generics
- Introduction to Generics
- TypeScript Generic Functions
- TypeScript Generic Classes
- TypeScript Generic Constraints
- TypeScript Modules
- Introduction to Modules
- TypeScript Import and Export
- TypeScript Default Exports
- TypeScript Namespace
- Decorators
- Introduction to Decorators
- TypeScript Class Decorators
- TypeScript Method Decorators
- TypeScript Property Decorators
- TypeScript Parameter Decorators
- Configuration
- TypeScript tsconfig.json File
- TypeScript Compiler Options
- TypeScript Strict Mode
- TypeScript Watch Mode
Access Modifiers in TypeScript
In object-oriented programming, one of the most important concepts is encapsulation. This is the practice of bundling data and the methods that operate on that data into a single unit (a class) and restricting access to the inner workings of that class. Access modifiers in TypeScript are the tools that allow you to do exactly this.
By using access modifiers, you define a "contract" for how other parts of your code can interact with your objects. This prevents accidental data corruption and makes your codebase much easier to maintain as it grows.
Key Access Modifiers:
- public: The "open door" policy.
- private: Strictly for internal use only.
- protected: Shared only with family (subclasses).
public. This is different from languages like Java or C#, where defaults can vary.
public
The public modifier is the default state for class members. A public property or method can be accessed from anywhere—inside the class, by instances of the class, and by any other part of your application. While it is the default, some developers choose to explicitly write public to make their intentions clear to other teammates.
Example:
class Car {
public make: string;
public model: string;
constructor(make: string, model: string) {
this.make = make;
this.model = model;
}
public displayInfo(): void {
console.log(`Make: ${this.make}, Model: ${this.model}`);
}
}
const myCar = new Car("Toyota", "Corolla");
console.log(myCar.make); // Accessible outside the class
myCar.displayInfo(); // Accessible outside the class
In this example, because make, model, and displayInfo() are public, we can interact with them directly after creating a new Car. This is useful for data that needs to be visible to the rest of your app, such as a user's display name or a product's price.
public can improve code readability, especially in large projects where you want to distinguish between the public API of a class and its internal logic.
private
The private modifier is your primary tool for hiding complexity. When a member is marked as private, it is only visible within the class where it was defined. Even subclasses cannot touch it. This is perfect for "helper" methods or internal state that shouldn't be messed with by outside code.
Example:
class Employee {
private id: number;
private name: string;
private salary: number;
constructor(id: number, name: string, salary: number) {
this.id = id;
this.name = name;
this.salary = salary;
}
private calculateTax(): number {
return this.salary * 0.2; // Internal logic
}
public getTakeHomePay(): number {
return this.salary - this.calculateTax(); // Accessing private method internally
}
}
const emp = new Employee(1, "John", 50000);
// console.log(emp.salary); // Error: 'salary' is private
// emp.calculateTax(); // Error: 'calculateTax' is private
console.log(emp.getTakeHomePay()); // Valid: public method calls the private logic
In this scenario, we don't want someone to accidentally change an employee's salary from outside the class or manually call calculateTax. We "expose" only what is necessary (getTakeHomePay) and hide the rest.
private and protected keywords disappear. If you need true runtime privacy, consider using the modern JavaScript #privateField syntax.
this inside a subclass. If Manager extends Employee, the Manager class cannot access this.salary if it is marked as private in the parent.
protected
The protected modifier sits right in the middle. It acts like private because it prevents access from outside the class, but it acts like public for subclasses. Use protected when you want to allow child classes to use or override a property, but still keep it hidden from the "outside world."
Example:
class Animal {
protected species: string;
constructor(species: string) {
this.species = species;
}
}
class Dog extends Animal {
public breed: string;
constructor(species: string, breed: string) {
super(species);
this.breed = breed;
}
public getIdentity(): string {
// We can access 'species' here because Dog is a subclass of Animal
return `I am a ${this.species} of breed ${this.breed}`;
}
}
const myDog = new Dog("Canine", "Bulldog");
// console.log(myDog.species); // Error: Property 'species' is protected
console.log(myDog.getIdentity()); // Valid: Subclass method uses the protected property
By using protected, we ensure that while a Dog knows it is a "Canine," a random piece of code elsewhere in the app cannot change the species of our dog instance.
protected for methods that provide "base functionality" intended to be customized or extended by child classes, such as a base render() logic in a UI component.
Readonly
While not strictly an "access" modifier in the sense of visibility, readonly is a powerful "mutation" modifier. It allows you to make a property immutable. Once a readonly property is assigned a value (either at the point of declaration or inside the constructor), it can never be changed again.
Example:
class Circle {
readonly pi: number = 3.14159;
readonly radius: number;
constructor(radius: number) {
this.radius = radius;
}
}
const circle = new Circle(10);
console.log(circle.radius); // Output: 10
// circle.radius = 20; // Error: Cannot assign to 'radius' because it is read-only.
readonly for configuration values, ID strings, or any data that should remain constant for the entire lifecycle of the object. It makes your code much more predictable.
Summary
Mastering access modifiers is key to writing clean, professional TypeScript. They act as the "API" documentation for your classes, telling other developers what they should and shouldn't touch.
- public: The default. Visible everywhere. Use for the main features of your class.
- private: Only visible inside the class. Use for "secret" internal logic and state.
- protected: Visible inside the class and its children. Use for shared logic in inheritance.
- readonly: Prevents the value from being changed after the object is created.
constructor(private id: number) {}. This creates the property, sets its access level, and assigns the value automatically!