OOP
04 / 10

Classes and Objects

Classes and Objects

Classes are blueprints for creating objects. Objects are instances of classes that contain actual data and can perform actions.

Defining a Class

A class typically contains properties (data) and methods (functions) that operate on that data.

class Person {
  // Properties
  private firstName: string;
  private lastName: string;
  private age: number;
  private email: string;

  // Constructor
  constructor(firstName: string, lastName: string, age: number, email: string) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
    this.email = email;
  }

  // Getter methods
  public getFullName(): string {
    return `${this.firstName} ${this.lastName}`;
  }

  public getAge(): number {
    return this.age;
  }

  // Method
  public introduce(): void {
    console.log(`Hi, I'm ${this.getFullName()} and I'm ${this.age} years old.`);
  }

  public celebrateBirthday(): void {
    this.age++;
    console.log(`Happy Birthday! I'm now ${this.age} years old.`);
  }

  // Static method (belongs to class, not instance)
  public static compareAges(person1: Person, person2: Person): string {
    if (person1.age > person2.age) {
      return `${person1.getFullName()} is older`;
    } else if (person1.age < person2.age) {
      return `${person2.getFullName()} is older`;
    }
    return "They are the same age";
  }
}

// Creating objects (instances)
const john = new Person("John", "Doe", 30, "john@example.com");
const jane = new Person("Jane", "Smith", 25, "jane@example.com");

john.introduce(); // Hi, I'm John Doe and I'm 30 years old.
jane.introduce(); // Hi, I'm Jane Smith and I'm 25 years old.

console.log(Person.compareAges(john, jane)); // John Doe is older

Access Modifiers

Access modifiers control the visibility of class members:

  • public: Accessible from anywhere

  • private: Only accessible within the class

  • protected: Accessible within the class and its subclasses

class Employee {
  public id: number;           // Accessible everywhere
  private salary: number;       // Only within Employee class
  protected department: string; // Within Employee and subclasses

  constructor(id: number, salary: number, department: string) {
    this.id = id;
    this.salary = salary;
    this.department = department;
  }

  private calculateBonus(): number {
    return this.salary * 0.10;
  }

  public getPaymentInfo(): string {
    return `Salary: $${this.salary}, Bonus: $${this.calculateBonus()}`;
  }
}

class Manager extends Employee {
  private teamSize: number;

  constructor(id: number, salary: number, department: string, teamSize: number) {
    super(id, salary, department);
    this.teamSize = teamSize;
  }

  public getTeamInfo(): string {
    // Can access protected department, but not private salary
    return `Managing ${this.teamSize} people in ${this.department} department`;
  }
}

const emp = new Employee(1, 50000, "IT");
console.log(emp.id);                 // OK: public
console.log(emp.getPaymentInfo());   // OK: public method
// console.log(emp.salary);          // ERROR: private
// console.log(emp.calculateBonus()); // ERROR: private method

Constructors

Constructors are special methods that initialize new objects. They can be overloaded to provide different ways of creating objects.

class Product {
  constructor(
    public readonly id: string,
    public name: string,
    public price: number,
    public inStock: boolean = true
  ) {
    // Shorthand property initialization
  }

  // Factory method pattern for creating products
  static createFromJSON(json: any): Product {
    return new Product(
      json.id,
      json.name,
      json.price,
      json.inStock ?? true
    );
  }

  static createDiscountedProduct(name: string, originalPrice: number, discount: number): Product {
    const discountedPrice = originalPrice * (1 - discount);
    return new Product(
      `DISC-${Date.now()}`,
      `${name} (${discount * 100}% OFF)`,
      discountedPrice
    );
  }
}

// Different ways to create products
const product1 = new Product("P001", "Laptop", 999.99);
const product2 = Product.createFromJSON({
  id: "P002",
  name: "Mouse",
  price: 29.99,
  inStock: true
});
const product3 = Product.createDiscountedProduct("Keyboard", 79.99, 0.20);

console.log(product3.name);  // "Keyboard (20% OFF)"
console.log(product3.price); // 63.992

Getters and Setters

Getters and setters allow controlled access to private properties with validation and computed properties.

class Temperature {
  private celsius: number;

  constructor(celsius: number) {
    this.celsius = celsius;
  }

  // Getter: read like a property
  get fahrenheit(): number {
    return (this.celsius * 9/5) + 32;
  }

  // Setter: write like a property with validation
  set fahrenheit(value: number) {
    if (value < -459.67) {
      throw new Error("Temperature cannot be below absolute zero!");
    }
    this.celsius = (value - 32) * 5/9;
  }

  get kelvin(): number {
    return this.celsius + 273.15;
  }

  set kelvin(value: number) {
    if (value < 0) {
      throw new Error("Kelvin cannot be negative!");
    }
    this.celsius = value - 273.15;
  }

  // Method to display all formats
  display(): void {
    console.log(`${this.celsius.toFixed(2)}°C = ${this.fahrenheit.toFixed(2)}°F = ${this.kelvin.toFixed(2)}K`);
  }
}

const temp = new Temperature(25);
temp.display(); // 25.00°C = 77.00°F = 298.15K

temp.fahrenheit = 98.6; // Set using Fahrenheit
temp.display(); // 37.00°C = 98.60°F = 310.15K

temp.kelvin = 273.15; // Set using Kelvin
temp.display(); // 0.00°C = 32.00°F = 273.15K

Static Members

Static members belong to the class itself rather than to instances of the class. They are useful for utility functions and shared data.

class MathUtils {
  // Static property
  static readonly PI = 3.14159265359;

  // Static method
  static circleArea(radius: number): number {
    return this.PI * radius ** 2;
  }

  static circleCircumference(radius: number): number {
    return 2 * this.PI * radius;
  }

  static degreesToRadians(degrees: number): number {
    return degrees * (this.PI / 180);
  }

  static radiansToDegrees(radians: number): number {
    return radians * (180 / this.PI);
  }
}

// Use without creating an instance
console.log(MathUtils.circleArea(5));           // 78.54
console.log(MathUtils.degreesToRadians(180));   // 3.14

class Counter {
  private static count: number = 0;
  public readonly id: number;

  constructor() {
    Counter.count++;
    this.id = Counter.count;
  }

  static getCount(): number {
    return Counter.count;
  }

  static reset(): void {
    Counter.count = 0;
  }
}

const c1 = new Counter();
const c2 = new Counter();
const c3 = new Counter();
console.log(Counter.getCount()); // 3
console.log(c1.id, c2.id, c3.id); // 1 2 3

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free