classes-basics-and-access-modifiers-private-public-protected
Classes: Basics and Access Modifiers (private
, public
, protected
)
Basic Class Declaration:
In TypeScript, you can declare a class using the
class
keyword. This creates a blueprint for objects, containing fields (properties) and methods.
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
Constructor Function:
The
constructor
is a special method for creating and initializing objects. It runs automatically when you instantiate a new object from a class.
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
draw() {
console.log(`Drawing at point (${this.x}, ${this.y})`);
}
}
public
Modifier:The
public
access modifier allows class members to be accessed from outside the class. By default, all members are public if you don't specify an access modifier.
class Greeter {
public greeting: string;
constructor(message: string) {
this.greeting = message;
}
public greet() {
return `Hello, ${this.greeting}`;
}
}
private
Modifier:The
private
access modifier restricts class members to be accessed only within the class itself. This helps with encapsulation.
class Counter {
private count = 0;
increment() {
this.count++;
}
getCount() {
return this.count;
}
}
protected
Modifier:The
protected
modifier allows class members to be accessed within the class and its subclasses. This is useful for inheritance.
class Person {
protected name: string;
constructor(name: string) {
this.name = name;
}
}
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
Encapsulation:
Access modifiers help in encapsulating data by restricting the level of visibility and access to class members. Encapsulation is a core principle of object-oriented programming.
class BankAccount {
private balance: number = 0;
deposit(amount: number) {
if (amount <= 0) throw new Error('Invalid amount');
this.balance += amount;
}
protected getBalance() {
return this.balance;
}
}
Instantiating Objects:
You can create an instance of a class using the
new
keyword. This will call the constructor and allocate memory for the object.
class Car {
constructor(public make: string, public model: string) {}
display() {
console.log(`Car: ${this.make} ${this.model}`);
}
}
let myCar = new Car('Toyota', 'Corolla');
myCar.display(); // Outputs: Car: Toyota Corolla
Method Overloading:
Method overloading allows a class to have multiple methods with the same name but different argument lists. It's like a swiss army knife, giving you different tools depending on what you need to do.
class Calculator {
add(a: number, b: number): number;
add(a: number, b: number, c: number): number;
add(a: number, b: number, c?: number): number {
if (c !== undefined) {
return a + b + c;
}
return a + b;
}
}
Static Members:
Static members belong to the class rather than any individual instance. It's like a family recipe that everyone in the family shares.
class Grid {
static origin = { x: 0, y: 0 };
calculateDistanceFromOrigin(point: { x: number; y: number }) {
let xDist = point.x - Grid.origin.x;
let yDist = point.y - Grid.origin.y;
return Math.sqrt(xDist * xDist + yDist * yDist);
}
}
Read-Only Properties:
Read-only properties must be initialized at their declaration or in the constructor. It's like a seal on a jar that can only be broken once.
class Job {
readonly title: string;
constructor(title: string) {
this.title = title;
}
}
Parameter Properties:
Parameter properties let you create and initialize member variables in one place. It's a shortcut to make your code cleaner.
class Animal {
constructor(private name: string, public habitat: string) {}
}
Inheritance:
Inheritance lets you create a new class based on an existing class. It's like a child inheriting traits from a parent.
class Animal {
move(distance: number) {}
}
class Dog extends Animal {
bark() {}
}
Abstract Classes:
Abstract classes are like blueprints. You can't build anything directly with them, but they can be used as a foundation for something else.
abstract class Department {
abstract generateReports(): void;
}
class AccountingDepartment extends Department {
generateReports() {
// Implementation here
}
}
Getters and Setters:
Getters and setters act as controls over how you access data within an object. It's like having a guard who checks your ID before you can enter a building.
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
this._fullName = newName;
}
}
Method Chaining:
Method chaining allows you to call multiple methods on the same object in a single line by having each method return the object itself.
class Calculator {
private value: number = 0;
add(n: number) {
this.value += n;
return this;
}
multiply(n: number) {
this.value *= n;
return this;
}
}
let calc = new Calculator().add(5).multiply(2);
Type Checking with
instanceof
:You can use the
instanceof
operator to check if an object is an instance of a particular class, aiding in type safety.
class Bird {
fly() {
console.log("Flying");
}
}
class Fish {
swim() {
console.log("Swimming");
}
}
function move(pet: Fish | Bird) {
if (pet instanceof Bird) {
pet.fly();
} else if (pet instanceof Fish) {
pet.swim();
}
}
const myBird = new Bird();
move(myBird); // Outputs: Flying
Polymorphism:
TypeScript supports polymorphism, allowing objects of different classes to be treated as objects of a shared superclass. This is useful for creating flexible and reusable code.
class Animal {
makeSound() {
console.log("Some sound");
}
}
class Dog extends Animal {
makeSound() {
console.log("Woof");
}
}
function playWithAnimal(animal: Animal) {
animal.makeSound();
}
const dog = new Dog();
playWithAnimal(dog); // Outputs: Woof
Singleton Pattern:
The singleton pattern restricts a class to a single instance and provides a way to access it. This is often implemented using a static method.
class Singleton {
private static instance: Singleton;
private constructor() {
// Private constructor to prevent direct construction calls with the `new` operator.
}
static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // Outputs: true
Type Compatibility:
TypeScript classes are structurally typed. This means that two different classes are compatible if their structure is the same, even if they don't explicitly inherit from each other.
class Point2D {
x: number;
y: number;
}
class Point3D {
x: number;
y: number;
z: number;
}
let point2d: Point2D = { x: 0, y: 0 };
let point3d: Point3D = { x: 0, y: 0, z: 0 };
// This works in TypeScript because the structure of Point3D has at least what Point2D has.
point2d = point3d;
Access Modifiers in Constructor:
You can also use access modifiers in the constructor parameters to automatically create and initialize class properties, streamlining your code.
class Car {
constructor(public make: string, public model: string) {}
}
const myCar = new Car('Honda', 'Accord');
console.log(myCar.make); // Outputs: Honda
// Here, `make` and `model` are automatically created as public fields.