Object-Oriented Programming (OOP) is a programming paradigm centered around objects that combine both data (properties) and behavior (methods). TypeScript, being a statically typed superset of JavaScript, brings several advanced OOP features to the table that enhance code quality, maintainability, and scalability.
A class in TypeScript is a blueprint for creating objects (instances). It defines properties and methods that the objects will have. Classes bring structure to TypeScript by enabling code reuse and encapsulation of logic.
Let’s review a basic class example before moving into inheritance.
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
introduce(): void {
console.log(`Hi, I am ${this.name}, and I am ${this.age} years old.`);
}
}
const person = new Person("Alice", 30);
person.introduce(); // Output: Hi, I am Alice, and I am 30 years old.
name
and age
are properties.constructor()
initializes the properties when a new object is created.introduce()
is a method that logs a message to the console.
Hi, I am Alice, and I am 30 years old.
Inheritance allows one class (child class) to inherit the properties and methods of another class (parent class). This enables code reuse and the extension of class functionality.
class Employee extends Person {
jobTitle: string;
constructor(name: string, age: number, jobTitle: string) {
super(name, age);
this.jobTitle = jobTitle;
}
work(): void {
console.log(`${this.name} is working as a ${this.jobTitle}.`);
}
}
const employee = new Employee("Bob", 35, "Software Engineer");
employee.introduce(); // Output: Hi, I am Bob, and I am 35 years old.
employee.work(); // Output: Bob is working as a Software Engineer.
extends
is used to establish inheritance between Person
and Employee
.super()
keyword is used to call the parent class’s constructor.Employee
class inherits the introduce()
method from Person
.
Hi, I am Bob, and I am 35 years old.
Bob is working as a Software Engineer.
In TypeScript, a child class can override the methods of its parent class to provide specific functionality.
class Manager extends Employee {
constructor(name: string, age: number, jobTitle: string) {
super(name, age, jobTitle);
}
// Override the work method
work(): void {
console.log(`${this.name} is managing the team.`);
}
}
const manager = new Manager("Charlie", 40, "Project Manager");
manager.work(); // Output: Charlie is managing the team.
Manager
class overrides the work()
method to provide its own implementation.
Charlie is managing the team.
Encapsulation is the concept of bundling data (properties) and methods that operate on the data within one unit, usually a class, and controlling the access to that data.
In TypeScript, encapsulation is achieved through access modifiers like:
public
: The property or method is accessible everywhere.private
: The property or method is accessible only within the class it is declared.protected
: The property or method is accessible within the class and its subclasses.
class BankAccount {
public owner: string;
private balance: number;
constructor(owner: string, balance: number) {
this.owner = owner;
this.balance = balance;
}
public deposit(amount: number): void {
this.balance += amount;
console.log(`Deposited ${amount}. New balance: ${this.balance}`);
}
public withdraw(amount: number): void {
if (amount <= this.balance) {
this.balance -= amount;
console.log(`Withdrew ${amount}. Remaining balance: ${this.balance}`);
} else {
console.log("Insufficient funds");
}
}
private showBalance(): void {
console.log(`Account balance: ${this.balance}`);
}
}
const account = new BankAccount("Alice", 1000);
account.deposit(200); // Deposited 200. New balance: 1200
account.withdraw(500); // Withdrew 500. Remaining balance: 700
// account.showBalance(); // Error: Property 'showBalance' is private.
balance
is marked as private
, so it can’t be accessed from outside the class.deposit
and withdraw
allow safe manipulation of the account balance.
Deposited 200. New balance: 1200
Withdrew 500. Remaining balance: 700
An abstract class is a class that cannot be instantiated directly. It can only be used as a base class and typically contains one or more abstract methods that must be implemented in any derived class.
abstract class Animal {
constructor(public name: string) {}
abstract makeSound(): void;
move(): void {
console.log(`${this.name} is moving.`);
}
}
class Dog extends Animal {
makeSound(): void {
console.log(`${this.name} says: Woof!`);
}
}
const dog = new Dog("Buddy");
dog.makeSound(); // Output: Buddy says: Woof!
dog.move(); // Output: Buddy is moving.
Animal
is an abstract class with an abstract method makeSound()
.Dog
class extends Animal
and provides an implementation for the makeSound()
method.
Buddy says: Woof!
Buddy is moving.
An interface in TypeScript defines a contract for classes to follow. It specifies what properties and methods a class should have but doesn’t provide implementations.
interface Drivable {
startEngine(): void;
stopEngine(): void;
}
class Car implements Drivable {
startEngine(): void {
console.log("Engine started.");
}
stopEngine(): void {
console.log("Engine stopped.");
}
}
const myCar = new Car();
myCar.startEngine(); // Output: Engine started.
myCar.stopEngine(); // Output: Engine stopped.
Drivable
is an interface that defines the methods startEngine
and stopEngine
.Car
class implements this interface and provides the required method implementations.
Engine started.
Engine stopped.
Polymorphism is the ability of different classes to be treated as instances of the same class through inheritance. It allows one method to be used in different ways depending on the object calling it.
class Shape {
area(): number {
return 0;
}
}
class Rectangle extends Shape {
constructor(public width: number, public height: number) {
super();
}
area(): number {
return this.width * this.height;
}
}
class Circle extends Shape {
constructor(public radius: number) {
super();
}
area(): number {
return Math.PI * this.radius * this.radius;
}
}
let shapes: Shape[] = [new Rectangle(5, 10), new Circle(7)];
shapes.forEach((shape) => {
console.log(shape.area());
});
Shape
is the base class with an area()
method.Rectangle
and Circle
are subclasses that provide their own implementation of the area()
method.Shape
class and call the area()
method without knowing the exact type of shape.
50
153.93804002589985
Generics allow you to create reusable components that can work with a variety of types while retaining type safety.
class Box {
content: T;
constructor(content: T) {
this.content = content;
}
getContent(): T {
return this.content;
}
}
let numberBox = new Box(100);
console.log(numberBox.getContent()); // Output: 100
let stringBox = new Box("TypeScript");
console.log(stringBox.getContent()); // Output: TypeScript
Box<T>
is a generic class where T
can be any type.Box
, one for a number and one for a string, demonstrating type flexibility.
100
TypeScript
Decorators are a special kind of declaration that can be attached to a class, method, or property to modify its behavior. Decorators are frequently used in frameworks like Angular.
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Called: ${propertyKey} with args: ${JSON.stringify(args)}`);
return originalMethod.apply(this, args);
};
}
class MathOperations {
@log
add(a: number, b: number): number {
return a + b;
}
}
const math = new MathOperations();
math.add(5, 3); // Output: Called: add with args: [5,3]
log
decorator logs the method name and arguments every time the add()
method is called.
Called: add with args: [5,3]
We’ve covered advanced OOP concepts in Inheritance: Allowing one class to inherit properties and methods from another. Encapsulation: Protecting internal data using access modifiers. Abstract Classes: Providing a blueprint for subclasses. Interfaces: Defining contracts for classes. Polymorphism: Allowing different classes to be treated as the same type. Generics: Enabling type-safe reuse of code. Decorators: Modifying class behavior in a clean and declarative way. Happy Coding!❤️