Programming Language
TypeScript
OOP

TypeScript Object-Oriented Programming (OOP) Concepts

Classes

TypeScript Classes

A class in TypeScript is a blueprint for creating objects. It defines properties and methods that the object can use.

Syntax to Declare a Class

class Person {
    name: string;
    age: number;
 
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
 
    displayInfo(): void {
        console.log(`Name: ${this.name}, Age: ${this.age}`);
    }
}

Creating an Object

let person1 = new Person("John", 30);
person1.displayInfo(); // Output: Name: John, Age: 30

Object Initialization Objects can be initialized in different ways:

By Reference Variable

let person2: Person = person1;
person2.displayInfo(); // Output: Name: John, Age: 30

By Method

class Person {
    constructor(public name: string, public age: number) {}
 
    static createPerson(name: string, age: number): Person {
        return new Person(name, age);
    }
}
 
let person3 = Person.createPerson("Alice", 25);
person3.displayInfo(); // Output: Name: Alice, Age: 25

By Constructor

let person4 = new Person("Eve", 22);
person4.displayInfo(); // Output: Name: Eve, Age: 22

Getters and Setters

You can use getters and setters to define special methods that control access to object properties.

class Person {
    private _age: number;
 
    constructor(public name: string, age: number) {
        this._age = age;
    }
 
    get age(): number {
        return this._age;
    }
 
    set age(newAge: number) {
        if (newAge >= 0) {
            this._age = newAge;
        } else {
            console.log("Age cannot be negative");
        }
    }
}
 
let person5 = new Person("Bob", 30);
console.log(person5.age); // Output: 30
person5.age = 35;
console.log(person5.age); // Output: 35
person5.age = -5; // Output: Age cannot be negative

Objects

What Are Objects in TypeScript?

An object in TypeScript is an instance of a class or a custom data structure that can hold properties and methods.

let person = {
    name: "John",
    age: 30
};

Property Modifiers in Objects

Optional Property

let person = {
    name: "John",
    age?: number // Optional
};

Readonly Property

let person = {
    readonly name: "John",
    age: 30
};

Declaring a Variable that Holds an Object

let person: { name: string, age: number } = { name: "John", age: 30 };

Object vs. Object in TypeScript

  • Object refers to the general object type.
  • {} refers to an object without any specific properties.
let obj1: Object = {};
let obj2: {} = {};

Empty Type in TypeScript

An empty type {} is a type with no properties.

let emptyObj: {} = {};

Access Modifiers

What Are Access Modifiers?

Access modifiers control the visibility of class members (properties or methods).

Public Access Modifier

A property or method with the public modifier can be accessed from anywhere.

class Person {
    public name: string;
 
    constructor(name: string) {
        this.name = name;
    }
}
 
let person = new Person("John");
console.log(person.name); // Output: John

Private Access Modifier

A property or method with the private modifier can only be accessed within the class.

class Person {
    private name: string;
 
    constructor(name: string) {
        this.name = name;
    }
 
    displayName() {
        console.log(this.name); // Can access private property
    }
}
 
let person = new Person("John");
// console.log(person.name); // Error: Property 'name' is private and only accessible within class 'Person'.
person.displayName(); // Output: John

Protected Access Modifier

A property or method with the protected modifier can be accessed within the class and by subclasses.

class Employee extends Person {
    protected position: string;
 
    constructor(name: string, position: string) {
        super(name);
        this.position = position;
    }
 
    displayPosition() {
        console.log(this.position); // Can access protected property
    }
}
 
let employee = new Employee("Jane", "Developer");
employee.displayPosition(); // Output: Developer

Readonly Modifier

The readonly modifier makes properties immutable.

class Person {
    readonly name: string;
 
    constructor(name: string) {
        this.name = name;
    }
}
 
let person = new Person("John");
// person.name = "Alice"; // Error: Cannot assign to 'name' because it is a read-only property.

Static Methods and Properties

Static Methods

Static methods are called on the class itself, not on instances.

class MathOperations {
    static add(a: number, b: number): number {
        return a + b;
    }
}
 
console.log(MathOperations.add(2, 3)); // Output: 5

Static Properties

Static properties are shared by all instances of a class.

class Counter {
    static count: number = 0;
 
    static increment() {
        this.count++;
    }
}
 
Counter.increment();
console.log(Counter.count); // Output: 1

as Keyword

Different Types of Keywords in TypeScript

The as keyword is used for type assertions.

let value: any = "Hello, world!";
let length: number = (value as string).length;
console.log(length); // Output: 13

this Keyword

The this keyword refers to the current instance of the class.

class Person {
    name: string;
 
    constructor(name: string) {
        this.name = name;
    }
 
    greet() {
        console.log(`Hello, ${this.name}`);
    }
}
 
let person = new Person("John");
person.greet(); // Output: Hello, John

Function Declaration Keywords

TypeScript uses function to declare a function.

function greet(name: string): void {
    console.log(`Hello, ${name}`);
}

Data Type Keywords

TypeScript provides types like string, number, boolean, etc., for variable declaration.

let age: number = 25;
let name: string = "John";

Variable Declaration Keywords

TypeScript allows let, const, and var for variable declarations.

let person = "John";
const age = 30;

as Keyword in TypeScript

As mentioned, the as keyword is used for type assertions, which tells the compiler to treat a value as a specific type.

let value: unknown = "Hello!";
let strLength: number = (value as string).length;

Inheritance

Types of Inheritance Single Inheritance A class can inherit from only one class.

class Animal {
    sound(): void {
        console.log("Some sound");
    }
}
 
class Dog extends Animal {
    sound(): void {
        console.log("Bark");
    }
}
 
let dog = new Dog();
dog.sound(); // Output: Bark

Hierarchical Inheritance

Multiple classes inherit from a single class.

class Animal {
    sound(): void {
        console.log("Some sound");
    }
}
 
class Dog extends Animal {
    sound(): void {
        console.log("Bark");
    }
}
 
class Cat extends Animal {
    sound(): void {
        console.log("Meow");
    }
}
 
let dog = new Dog();
dog.sound(); // Output: Bark
let cat = new Cat();
cat.sound(); // Output: Meow

Multilevel Inheritance

A class inherits from another class, which is also inherited by another class.

class Animal {
    sound(): void {
        console.log("Some sound");
    }
}
 
class Dog extends Animal {
    sound(): void {
        console.log("Bark");
    }
}
 
class Puppy extends Dog {
    sound(): void {
        console.log("Puppy Bark");
    }
}
 
let puppy = new Puppy();
puppy.sound(); // Output: Puppy Bark

Duck Typing

Duck typing refers to the concept where the type of an object is determined by its properties and methods, not its class.

interface Duck {
    quack(): void;
}
 
class RealDuck implements Duck {
    quack() {
        console.log("Quack");
    }
}
 
class FakeDuck {
    quack() {
        console.log("Fake Quack");
    }
}
 
function makeQuack(duck: Duck) {
    duck.quack();
}
 
makeQuack(new RealDuck()); // Output: Quack
makeQuack(new FakeDuck()); // Output: Fake Quack

Mixin

Mixins allow adding properties or methods to a class.

class Walker {
    walk() {
        console.log("Walking...");
    }
}
 
class Runner {
    run() {
        console.log("Running...");
    }
}
 
class Athlete implements Walker, Runner {
    walk: () => void;
    run: () => void;
}
 
Object.assign(Athlete.prototype, Walker.prototype, Runner.prototype);
 
let athlete = new Athlete();
athlete.walk(); // Output: Walking...
athlete.run(); // Output: Running...

Abstract Classes

Abstract classes cannot be instantiated and are meant to be inherited.

abstract class Animal {
    abstract sound(): void;
}
 
class Dog extends Animal {
    sound(): void {
        console.log("Bark");
    }
}
 
let dog = new Dog();
dog.sound(); // Output: Bark

Interfaces

Interfaces define the structure of an object or class.

interface Person {
    name: string;
    age: number;
}
 
let person: Person = { name: "John", age: 30 };

Generics

Generic Classes

class Box<T> {
    private value: T;
 
    constructor(value: T) {
        this.value = value;
    }
 
    getValue(): T {
        return this.value;
    }
}
 
let numberBox = new Box<number>(42);
console.log(numberBox.getValue()); // Output: 42

Generic Interfaces

interface Container<T> {
    value: T;
}
 
let container: Container<string> = { value: "Hello" };

Decorators

Decorators are special types of declarations that can be attached to classes, methods, or properties to modify their behavior.

function log(target: any, key: string) {
    console.log(`Property ${key} is being accessed`);
}
 
class Person {
    @log
    name: string;
 
    constructor(name: string) {
        this.name = name;
    }
}
 
let person = new Person("John");
console.log(person.name); // Output: Property name is being accessed

Decorators Class Methods

function methodLog(target: any, methodName: string, descriptor: PropertyDescriptor) {
    let originalMethod = descriptor.value;
 
    descriptor.value = function (...args: any[]) {
        console.log(`Method ${methodName} called with arguments: ${args}`);
        return originalMethod.apply(this, args);
    };
}
 
class Person {
    @methodLog
    greet(name: string) {
        console.log(`Hello, ${name}`);
    }
}
 
let person = new Person();
person.greet("John"); // Output: Method greet called with arguments: [ 'John' ]
                        // Output: Hello, John