Learn how to extend classes in TypeScript to create subclasses, and understand the concepts of inheritance and polymorphism in object-oriented programming.
In this section, we’ll delve into the concept of inheritance in TypeScript, a fundamental principle of object-oriented programming (OOP). Inheritance allows us to create new classes based on existing ones, promoting code reuse and a more organized code structure. We’ll explore how to use the extends
keyword to create subclasses, understand how derived classes inherit and override properties and methods, and discuss the role of polymorphism in OOP.
Inheritance is a mechanism that allows one class (the derived class) to inherit properties and methods from another class (the base class). This enables us to create a hierarchy of classes that share common functionality, reducing code duplication and enhancing maintainability.
extends
KeywordIn TypeScript, we use the extends
keyword to create a derived class from a base class. The derived class inherits all the properties and methods of the base class, allowing us to build upon existing functionality.
Let’s start with a simple example to illustrate how inheritance works in TypeScript.
// Base class
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log("Some generic animal sound");
}
}
// Derived class
class Dog extends Animal {
constructor(name: string) {
super(name); // Call the constructor of the base class
}
makeSound(): void {
console.log("Bark!");
}
}
const dog = new Dog("Buddy");
dog.makeSound(); // Output: Bark!
In this example, we have a base class Animal
with a property name
and a method makeSound()
. The Dog
class extends Animal
, inheriting its properties and methods. The Dog
class overrides the makeSound()
method to provide a specific implementation.
When a derived class inherits from a base class, it gains access to all the public and protected properties and methods of the base class. The derived class can also override methods to provide its own implementation.
Overriding allows a derived class to provide a specific implementation of a method that is already defined in its base class. This is useful when the derived class needs to alter or extend the behavior of the base class method.
class Cat extends Animal {
constructor(name: string) {
super(name);
}
makeSound(): void {
console.log("Meow!");
}
}
const cat = new Cat("Whiskers");
cat.makeSound(); // Output: Meow!
Here, the Cat
class overrides the makeSound()
method to produce a “Meow!” sound, demonstrating how derived classes can customize inherited methods.
Polymorphism is a core concept in OOP that allows objects of different classes to be treated as objects of a common superclass. It enables us to write more flexible and reusable code.
Let’s see how polymorphism works with our Animal
example.
function makeAnimalSound(animal: Animal): void {
animal.makeSound();
}
const animals: Animal[] = [new Dog("Buddy"), new Cat("Whiskers")];
animals.forEach(makeAnimalSound);
// Output:
// Bark!
// Meow!
In this example, the makeAnimalSound
function accepts an Animal
type, allowing us to pass any object that is an instance of Animal
or its derived classes. This demonstrates polymorphism, where the actual method that gets called depends on the object’s runtime type.
To better understand the relationship between base and derived classes, let’s visualize the class hierarchy using a diagram.
classDiagram class Animal { +name: string +makeSound(): void } class Dog { +makeSound(): void } class Cat { +makeSound(): void } Animal <|-- Dog Animal <|-- Cat
In this diagram, Animal
is the base class, and Dog
and Cat
are derived classes. The arrows indicate the inheritance relationship.
Let’s consider a more practical example involving a vehicle hierarchy.
// Base class
class Vehicle {
brand: string;
model: string;
constructor(brand: string, model: string) {
this.brand = brand;
this.model = model;
}
startEngine(): void {
console.log("Engine started");
}
}
// Derived class
class Car extends Vehicle {
numberOfDoors: number;
constructor(brand: string, model: string, numberOfDoors: number) {
super(brand, model);
this.numberOfDoors = numberOfDoors;
}
startEngine(): void {
console.log("Car engine started");
}
}
const myCar = new Car("Toyota", "Corolla", 4);
myCar.startEngine(); // Output: Car engine started
In this example, Vehicle
is the base class with properties brand
and model
, and a method startEngine()
. The Car
class extends Vehicle
, adding a new property numberOfDoors
and overriding the startEngine()
method.
To reinforce your understanding, try modifying the code examples:
Truck
that extends Vehicle
and includes a property cargoCapacity
.startEngine()
method in Truck
to print a different message.Car
and Truck
, and call their startEngine()
methods.extends
keyword is used to create a derived class from a base class.