Skip to main content

Module 5: Classes in TypeScript

Classes are fundamental to Object-Oriented Programming (OOP) in TypeScript. This module covers class syntax, access modifiers, inheritance, and advanced class patterns.


1. Basic Class Syntax

class Person {
name: string;
age: number;

constructor(name: string, age: number) {
this.name = name;
this.age = age;
}

greet(): string {
return `Hello, I'm ${this.name} and I'm ${this.age} years old.`;
}
}

let person = new Person("Alice", 25);
console.log(person.greet()); // "Hello, I'm Alice and I'm 25 years old."

2. Access Modifiers

TypeScript provides three access modifiers to control visibility.

Public (Default)

class Employee {
public name: string;
public salary: number;

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

let emp = new Employee("Bob", 50000);
console.log(emp.name); // Accessible

Private

class BankAccount {
private balance: number;

constructor(initialBalance: number) {
this.balance = initialBalance;
}

public deposit(amount: number): void {
this.balance += amount;
}

public getBalance(): number {
return this.balance;
}
}

let account = new BankAccount(1000);
// account.balance; // ❌ Error: Property 'balance' is private
account.deposit(500);
console.log(account.getBalance()); // 1500

Protected

class Vehicle {
protected speed: number = 0;

accelerate(amount: number): void {
this.speed += amount;
}
}

class Car extends Vehicle {
showSpeed(): void {
console.log(`Speed: ${this.speed} km/h`); // ✅ OK in subclass
}
}

let car = new Car();
car.accelerate(50);
car.showSpeed(); // "Speed: 50 km/h"
// car.speed; // ❌ Error: Property 'speed' is protected
Access Modifier Summary
  • public: Accessible everywhere (default)
  • private: Accessible only within the class
  • protected: Accessible within the class and subclasses

3. Shorthand Constructor

class User {
constructor(
public name: string,
public email: string,
private password: string
) {}
}

let user = new User("Alice", "alice@example.com", "secret123");
console.log(user.name); // ✅ Accessible
console.log(user.email); // ✅ Accessible
// console.log(user.password); // ❌ Error: private

4. Readonly Properties

class Product {
readonly id: number;
name: string;

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

updateName(newName: string): void {
this.name = newName; // ✅ OK
// this.id = 123; // ❌ Error: Cannot assign to 'id'
}
}

5. Getters and Setters

class Temperature {
private _celsius: number = 0;

get celsius(): number {
return this._celsius;
}

set celsius(value: number) {
if (value < -273.15) {
throw new Error("Temperature below absolute zero!");
}
this._celsius = value;
}

get fahrenheit(): number {
return this._celsius * 9 / 5 + 32;
}

set fahrenheit(value: number) {
this.celsius = (value - 32) * 5 / 9;
}
}

let temp = new Temperature();
temp.celsius = 25;
console.log(temp.fahrenheit); // 77

6. Static Members

Static members belong to the class itself, not instances.

class MathUtils {
static PI: number = 3.14159;

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

static squareArea(side: number): number {
return side * side;
}
}

console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.circleArea(5)); // 78.53975

7. Inheritance

class Animal {
constructor(public name: string) {}

move(distance: number = 0): void {
console.log(`${this.name} moved ${distance}m.`);
}
}

class Dog extends Animal {
bark(): void {
console.log("Woof! Woof!");
}
}

class Bird extends Animal {
fly(distance: number): void {
console.log(`${this.name} flew ${distance}m.`);
}
}

let dog = new Dog("Rex");
dog.bark(); // "Woof! Woof!"
dog.move(10); // "Rex moved 10m."

let bird = new Bird("Tweety");
bird.fly(50); // "Tweety flew 50m."

8. Method Overriding

class Shape {
area(): number {
return 0;
}
}

class Circle extends Shape {
constructor(public radius: number) {
super();
}

area(): number {
return Math.PI * this.radius * this.radius;
}
}

class Rectangle extends Shape {
constructor(public width: number, public height: number) {
super();
}

area(): number {
return this.width * this.height;
}
}

let circle = new Circle(5);
console.log(circle.area()); // 78.53981633974483

let rect = new Rectangle(10, 20);
console.log(rect.area()); // 200

9. Abstract Classes

Abstract classes cannot be instantiated and serve as base classes.

abstract class Employee {
constructor(public name: string, public id: number) {}

abstract calculateSalary(): number;

displayInfo(): void {
console.log(`${this.name} (ID: ${this.id})`);
}
}

class FullTimeEmployee extends Employee {
constructor(name: string, id: number, public monthlySalary: number) {
super(name, id);
}

calculateSalary(): number {
return this.monthlySalary * 12;
}
}

class Contractor extends Employee {
constructor(
name: string,
id: number,
public hourlyRate: number,
public hoursWorked: number
) {
super(name, id);
}

calculateSalary(): number {
return this.hourlyRate * this.hoursWorked;
}
}

let emp1 = new FullTimeEmployee("Alice", 101, 5000);
console.log(emp1.calculateSalary()); // 60000

let emp2 = new Contractor("Bob", 102, 50, 160);
console.log(emp2.calculateSalary()); // 8000
Abstract Class Rules
  • Cannot instantiate abstract classes
  • Must implement all abstract methods in subclasses
  • Can have both abstract and concrete methods

10. Implementing Interfaces

interface Printable {
print(): void;
}

interface Loggable {
log(): void;
}

class Document implements Printable, Loggable {
constructor(public content: string) {}

print(): void {
console.log(`Printing: ${this.content}`);
}

log(): void {
console.log(`Log: ${this.content}`);
}
}

let doc = new Document("Hello World");
doc.print(); // "Printing: Hello World"
doc.log(); // "Log: Hello World"

11. Class Expressions

const Rectangle = class {
constructor(public width: number, public height: number) {}

area(): number {
return this.width * this.height;
}
};

let rect = new Rectangle(10, 20);
console.log(rect.area()); // 200

12. Singleton Pattern

class Database {
private static instance: Database;
private constructor() {
console.log("Database initialized");
}

static getInstance(): Database {
if (!Database.instance) {
Database.instance = new Database();
}
return Database.instance;
}

query(sql: string): void {
console.log(`Executing: ${sql}`);
}
}

let db1 = Database.getInstance(); // "Database initialized"
let db2 = Database.getInstance(); // No new initialization
console.log(db1 === db2); // true

Key Takeaways

✅ Classes support encapsulation with access modifiers
✅ Use inheritance to create class hierarchies
Abstract classes define contracts for subclasses
Static members belong to the class, not instances
Getters/setters provide controlled access to properties
✅ Classes can implement multiple interfaces


Practice Exercises

Exercise 1: Create a Car Class

class Car {
constructor(
public brand: string,
public model: string,
private mileage: number = 0
) {}

drive(distance: number): void {
this.mileage += distance;
}

getMileage(): number {
return this.mileage;
}
}

Exercise 2: Library System

Create a Book class and Library class that manages a collection of books.

class Book {
constructor(
public title: string,
public author: string,
public isbn: string
) {}
}

class Library {
private books: Book[] = [];

addBook(book: Book): void {
this.books.push(book);
}

findBookByISBN(isbn: string): Book | undefined {
return this.books.find(book => book.isbn === isbn);
}
}

Exercise 3: Implement a Shape Hierarchy

Create an abstract Shape class and implement Circle and Square classes.


Next Steps

In Module 6, we'll explore Generics and learn how to create reusable, type-safe components that work with multiple types.