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
- 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
- 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.