Skip to main content

classes-basics-and-access-modifiers-private-public-protected

Classes: Basics and Access Modifiers (private, public, protected)

  1. 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.`);
}
}
  1. 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})`);
}
}
  1. 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}`;
}
}
  1. 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;
}
}
  1. 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}.`;
}
}
  1. 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;
}
}
  1. 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);
  1. 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
  1. 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
  1. 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
  1. 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;
  1. 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.