Object-Oriented Programming (OOP) is a powerful paradigm that allows developers to model real-world entities using objects and classes. This approach helps in creating modular, scalable, and maintainable code. In this comprehensive guide, we will delve into the fundamental concepts of OOP using TypeScript examples. By the end of this article, you will have a thorough understanding of classes, objects, inheritance, polymorphism, encapsulation, and abstraction in TypeScript.
Introduction to Object-Oriented Programming
OOP is a programming paradigm centered around objects rather than actions. It allows us to bundle data and methods that operate on that data into a single unit called an object. This encapsulation fosters modularity and reusability.
What is TypeScript?
TypeScript is a statically typed superset of JavaScript that compiles to plain JavaScript. It brings type-safety to JavaScript, allowing for early error detection and improved development experience.
Classes and Objects
Defining a Class
A class is a blueprint for creating objects. It defines properties and methods that the created objects will have. In TypeScript, a class is defined using the class
keyword.
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
In the example above, we define a Person
class with two properties: name
and age
. The constructor initializes these properties, and the greet
method outputs a greeting message.
Creating an Object
An object is an instance of a class. We create an object by using the new
keyword followed by the class name.
const person1 = new Person('Alice', 30);
person1.greet(); // Output: Hello, my name is Alice and I am 30 years old.
Inheritance
Inheritance is a mechanism where a new class (derived class) inherits the properties and methods of an existing class (base class). This promotes code reuse and establishes a relationship between classes.
Base and Derived Classes
In TypeScript, we use the extends
keyword to create a derived class.
class Employee extends Person {
employeeId: number;
constructor(name: string, age: number, employeeId: number) {
super(name, age);
this.employeeId = employeeId;
}
displayEmployeeInfo() {
console.log(`Employee ID: ${this.employeeId}, Name: ${this.name}, Age: ${this.age}`);
}
}
const employee1 = new Employee('Bob', 25, 1234);
employee1.displayEmployeeInfo(); // Output: Employee ID: 1234, Name: Bob, Age: 25
In this example, the Employee
class inherits from the Person
class. It adds a new property employeeId
and a method displayEmployeeInfo
. The super
keyword calls the constructor of the base class.
Polymorphism
Polymorphism allows objects of different classes to be treated as objects of a common superclass. It supports method overriding, where a derived class provides a specific implementation of a method already defined in its base class.
Method Overriding
In TypeScript, we override a method by defining it in the derived class with the same name as in the base class.
class Manager extends Employee {
department: string;
constructor(name: string, age: number, employeeId: number, department: string) {
super(name, age, employeeId);
this.department = department;
}
displayEmployeeInfo() {
console.log(`Manager ID: ${this.employeeId}, Name: ${this.name}, Age: ${this.age}, Department: ${this.department}`);
}
}
const manager1 = new Manager('Carol', 40, 5678, 'HR');
manager1.displayEmployeeInfo(); // Output: Manager ID: 5678, Name: Carol, Age: 40, Department: HR
Here, the Manager
class overrides the displayEmployeeInfo
method to include additional information specific to managers.
Encapsulation
Encapsulation is the practice of bundling data and methods that operate on that data within a class, restricting direct access to some of the class’s components. This is achieved through access modifiers: public
, private
, and protected
.
Access Modifiers
public
: Members are accessible from anywhere.private
: Members are accessible only within the class.protected
: Members are accessible within the class and its subclasses.
class BankAccount {
private balance: number;
constructor(initialBalance: number) {
this.balance = initialBalance;
}
deposit(amount: number) {
this.balance += amount;
}
withdraw(amount: number) {
if (amount <= this.balance) {
this.balance -= amount;
} else {
console.log('Insufficient balance');
}
}
getBalance() {
return this.balance;
}
}
const account = new BankAccount(1000);
account.deposit(500);
account.withdraw(200);
console.log(account.getBalance()); // Output: 1300
In this example, the balance
property is private, and can only be modified through the deposit
and withdraw
methods. This ensures controlled access to the account balance.
Abstraction
Abstraction is the concept of hiding complex implementation details and showing only the essential features of an object. It is achieved through abstract classes and interfaces.
Abstract Classes
An abstract class cannot be instantiated and is meant to be subclassed. It can contain abstract methods that must be implemented by derived classes.
abstract class Shape {
abstract calculateArea(): number;
displayArea() {
console.log(`The area is ${this.calculateArea()}`);
}
}
class Circle extends Shape {
radius: number;
constructor(radius: number) {
super();
this.radius = radius;
}
calculateArea(): number {
return Math.PI * Math.pow(this.radius, 2);
}
}
const circle = new Circle(5);
circle.displayArea(); // Output: The area is 78.53981633974483
In this example, the Shape
class is abstract and contains an abstract method calculateArea
. The Circle
class extends Shape
and provides an implementation for calculateArea
.
Interfaces
Interfaces define the structure that a class must adhere to. They cannot contain implementation details.
interface Vehicle {
make: string;
model: string;
startEngine(): void;
stopEngine(): void;
}
class Car implements Vehicle {
make: string;
model: string;
constructor(make: string, model: string) {
this.make = make;
this.model = model;
}
startEngine() {
console.log(`Starting engine of ${this.make} ${this.model}`);
}
stopEngine() {
console.log(`Stopping engine of ${this.make} ${this.model}`);
}
}
const myCar = new Car('Toyota', 'Corolla');
myCar.startEngine(); // Output: Starting engine of Toyota Corolla
myCar.stopEngine(); // Output: Stopping engine of Toyota Corolla
Here, the Vehicle
interface defines the structure for a vehicle, and the Car
class implements this interface, ensuring it adheres to the defined structure.
Advanced OOP Concepts
Static Members
Static members belong to the class itself rather than any instance of the class. They are accessed using the class name.
class MathUtils {
static PI: number = 3.14159;
static calculateCircumference(radius: number): number {
return 2 * MathUtils.PI * radius;
}
}
console.log(MathUtils.PI); // Output: 3.14159
console.log(MathUtils.calculateCircumference(5)); // Output: 31.4159
Getters and Setters
Getters and setters allow controlled access to the properties of a class.
class Student {
private _name: string;
constructor(name: string) {
this._name = name;
}
get name(): string {
return this._name;
}
set name(newName: string) {
if (newName.length > 0) {
this._name = newName;
} else {
console.log('Name cannot be empty');
}
}
}
const student = new Student('John');
console.log(student.name); // Output: John
student.name = 'Doe';
console.log(student.name); // Output: Doe
student.name = ''; // Output: Name cannot be empty
Conclusion
Object-Oriented Programming in TypeScript provides a robust framework for building scalable and maintainable applications. By understanding and applying the principles of classes, objects, inheritance, polymorphism, encapsulation, and abstraction, developers can create well-structured and efficient code. TypeScript enhances this paradigm with type safety, making it a preferred choice for many developers.
We hope this comprehensive guide has given you a clear understanding of the basic and advanced concepts of OOP in TypeScript. By mastering these concepts, you will be well-equipped to tackle complex programming challenges and develop high-quality software solutions.