Lesson 4
Inheritance in TypeScript classes
Introduction

Hello again! In this part of our journey into Object-Oriented Programming with TypeScript, we'll explore inheritance. Inheritance allows us to share code across classes, thus improving readability and efficiency.

TypeScript enhances this process with strong typing, which ensures reliability and consistent behavior across the application. By leveraging TypeScript's type system, we can reinforce the integrity of our inherited attributes and methods, providing a more robust programming experience.

In this lesson, we'll explore attribute and method inheritance in TypeScript using practical examples. Our lesson plan includes defining inheritance, examining attribute inheritance, exploring method inheritance, and understanding the super() function. Ready? Let's get started!

Defining Inheritance

Inheritance involves creating a child class that inherits properties and methods from a parent class. In TypeScript, we encounter scenarios where classes share common attributes or methods, making inheritance highly useful.

The extends keyword is used to set up inheritance, allowing one class to inherit properties and methods from another class. Here's an example featuring a parent class named Vehicle and a child class named Car, both using TypeScript syntax:

TypeScript
1// Define the parent class 'Vehicle' 2class Vehicle { 3 // Initialize the Vehicle with color and brand attributes 4 constructor(public color: string, public brand: string) {} 5} 6 7// Define the child class 'Car', inheriting from 'Vehicle' 8class Car extends Vehicle { 9 constructor(color: string, brand: string, public doors: number) { 10 // Call the parent class's constructor method to set color and brand 11 super(color, brand); 12 } 13}

In the Car class, the super() function inside its constructor calls the Vehicle class's constructor, enabling the inherited properties to be correctly initialized. The extends keyword signifies that Car is a subclass of Vehicle.

In this lesson, our focus will primarily be on single inheritance, where one parent class gives attributes and methods to one child class, highlighting the strengths of type-safe inheritance in TypeScript.

Attribute Inheritance

Attribute inheritance allows a child class to inherit the attributes of a parent class, with private fields being protected by TypeScript's access modifiers.

Consider this example featuring a parent class named Artist, and a child class named Musician:

TypeScript
1class Artist { 2 constructor(public name: string) {} // Parent's attribute 3} 4 5class Musician extends Artist { 6 constructor(name: string, public instrument: string) { 7 super(name); // Inheriting parent's attribute 8 } 9} 10 11const john = new Musician('John Lennon', 'Guitar'); // Creating a Musician instance 12console.log(john.name); // Output: John Lennon 13console.log(john.instrument); // Output: Guitar

However, if the name attribute in the Artist class were private, it wouldn't be directly accessible in the Musician class. Instead, it would be accessed via a method:

TypeScript
1class Artist { 2 private name: string; // Private attribute 3 4 constructor(name: string) { 5 this.name = name; // Initialize private attribute 6 } 7 8 getName(): string { // Getter method for the private attribute 9 return this.name; 10 } 11} 12 13class Musician extends Artist { 14 public instrument: string; 15 16 constructor(name: string, instrument: string) { 17 super(name); 18 this.instrument = instrument; // Initialize instrument attribute 19 } 20} 21 22const john = new Musician('John Lennon', 'Guitar'); 23console.log(john.getName()); // Output: John Lennon 24console.log(john.instrument); // Output: Guitar

The Musician class inherits the name attribute from the Artist class and also has its own unique attribute, instrument. Since name is private, it is accessed through the getName method.

Method Inheritance

Similar to attributes, method inheritance allows a child class to inherit the methods of a parent class. Type annotations enhance this by ensuring parameter and return types are clear and reliable.

Here's an example where the Car class can invoke the start method from the Vehicle class:

TypeScript
1class Vehicle { 2 constructor(public brand: string) {} 3 4 start(): void { 5 console.log(`The ${this.brand} is starting.`); 6 } 7} 8 9class Car extends Vehicle { 10 // No new methods or attributes are added here 11} 12 13const myCar = new Car('BMW'); 14myCar.start(); // Output: The BMW is starting.

In this scenario, TypeScript automatically provides a default constructor if none is defined, which internally calls super(). This ensures that the parent class's constructor is invoked correctly, facilitating seamless inheritance of the brand attribute in the Car class.

Understanding the 'super()' Function

The super() function is integral in inheritance for calling parent class methods from a child class. It is particularly useful in method overriding and initialization. It allows a child class to extend or utilize the functionality of a parent class without directly modifying it.

For instance, when overriding a method to add or alter its behavior, super() enables calling the original method from the parent class to integrate its functionality with new enhancements:

TypeScript
1class Vehicle { 2 start(): string { 3 return "Vehicle is starting..."; 4 } 5} 6 7class Car extends Vehicle { 8 start(): string { 9 return super.start() + " Beep! Beep!"; 10 } 11} 12 13const myCar = new Car(); 14console.log(myCar.start()); // Output: Vehicle is starting... Beep! Beep!

Similarly, during initialization, super() calls the constructor method of the parent class, ensuring that the child class is initialized correctly, allowing the child class to add its specific attributes seamlessly:

TypeScript
1class ParentClass { 2 value: string; 3 4 constructor(value: string) { 5 this.value = value; 6 } 7} 8 9class ChildClass extends ParentClass { 10 additionalValue: string; 11 12 constructor(value: string, additionalValue: string) { 13 super(value); // Invoke parent class's constructor 14 this.additionalValue = additionalValue; 15 } 16} 17 18const childClass = new ChildClass("value", "additional_value"); 19console.log(childClass.value); // Output: value 20console.log(childClass.additionalValue); // Output: additional_value

In these ways, super() facilitates a coherent and modular approach to inheritance by allowing child classes to build upon or adapt the functionality of their parent classes effectively and cleanly in TypeScript.

Lesson Summary

We've successfully explored attribute and method inheritance in TypeScript and practiced using several examples. Mastering these concepts can enhance both efficiency and readability. Practice is essential for proficiency, so get ready for some practice exercises to solidify your understanding. Programming is all about experimenting, learning, and problem-solving. Enjoy the journey!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.