Lesson 5
Understanding Inheritance in TypeScript
Introduction to Inheritance

Welcome back to our exploration of Object-Oriented Programming (OOP) in TypeScript. This unit's topic is Inheritance, a feature that can significantly enhance code reusability and efficiency.

Inheritance: First Example

Programming, especially OOP, often mirrors concepts found in the real world. Inheritance is one such trait - Child classes inherit characteristics from their Parent classes, akin to genetics in biology.

Here is a concrete TypeScript demonstration of a Child class inheriting from a Parent class:

TypeScript
1// Parent class 2class Parent { 3 name: string; // name property 4 5 constructor(name: string) { 6 this.name = name; 7 } 8 9 greet(): void { 10 console.log(`Hello, my name is ${this.name}`); // greet method 11 } 12} 13 14// 'Child' class extending 'Parent' 15class Child extends Parent { 16 age: number; // age property 17 18 constructor(name: string, age: number) { 19 super(name); 20 this.age = age; 21 } 22 23 info(): void { 24 console.log(`I am ${this.age} years old.`); // info method 25 } 26} 27 28const child = new Child('Alice', 10); 29child.greet(); // prints: Hello, my name is Alice 30child.info(); // prints: I am 10 years old.

In this instance, the Child class inherits from the Parent class, thereby sharing the greet() method. This is inheritance in action!

The 'extends' Keyword

We introduce the keyword extends for implementing inheritance. It's a potent keyword that allows a new class to inherit properties and methods from an existing class. In short, a Child class extends from a Parent class.

For example, a Car extends a Vehicle. While both have the potential for movement, a Car has additional specific attributes — such as the number of wheels.

TypeScript
1// Vehicle class 2class Vehicle { 3 name: string; // name property 4 speed: number; // speed property 5 6 constructor(name: string, speed: number) { 7 this.name = name; 8 this.speed = speed; 9 } 10 11 move(): void { 12 console.log(`${this.name} moves at ${this.speed} mph.`); // move method 13 } 14} 15 16// 'Car' class extending 'Vehicle' 17class Car extends Vehicle { 18 wheels: number; // new wheels property 19 20 constructor(name: string, speed: number, wheels: number) { 21 super(name, speed); // call to parent constructor 22 this.wheels = wheels; 23 } 24 25 specs(): void { 26 console.log(`I am a ${this.name} and I have ${this.wheels} wheels.`); // specs method 27 } 28} 29 30const myCar = new Car('Toyota', 120, 4); 31myCar.move(); // prints: Toyota moves at 120 mph 32myCar.specs(); // prints: I am a Toyota and I have 4 wheels.

In this demonstration, the Car class extends the Vehicle class, adopting the move() method.

Discovering the 'super' Keyword

The factor that unites the parent and child class is the super keyword. It calls the constructor of the parent class to enable the inheritance of its properties and methods. A real-world parallel would be an apple, a variety of fruit, inheriting from its parent category Fruit.

TypeScript
1class Fruit { 2 name: string; // name property 3 4 constructor(name: string) { 5 this.name = name; 6 console.log(`Fruit's constructor called with name=${name}`); 7 } 8 9 printName(): void { 10 console.log(`Fruit's name: ${this.name}`); 11 } 12} 13 14class Apple extends Fruit { 15 color: string; // color property 16 17 constructor(name: string, color: string) { 18 super(name); // calling parent class constructor 19 this.color = color; 20 console.log(`Apple's constructor called with name=${name}, color=${color}`); 21 } 22 23 printColor(): void { 24 console.log(`Color of ${this.name} is ${this.color}`); 25 } 26} 27 28let apple = new Apple('Apple', 'Red'); 29// Prints: 30// Fruit's constructor called with name=Apple 31// Apple's constructor called with name=Apple, color=Red 32 33apple.printName(); // Prints: Fruit's name: Apple 34apple.printColor(); // Prints: Color of Apple is Red

In the Apple class above, super calls the constructor of the Fruit class, inheriting the name property and the printName() method. If the Apple class didn't have any constructor, TypeScript would automatically call the parent's constructor when creating an instance of the Apple class.

Redefining and Overriding Methods using Inheritance

Occasionally during inheritance, a Child class might require a different method behavior than its Parent class. This is where method redefinition, or method overriding, comes into the picture.

Method overriding enables a Child class to offer a different implementation of methods that are already defined in its Parent class. Using the same method name and parameters in the Child class, the Parent class's method can be overridden.

Here's a practical example:

TypeScript
1class Parent { 2 greet(): void { 3 console.log('Hello from Parent'); 4 } 5} 6 7class Child extends Parent { 8 // Method override 9 greet(): void { 10 console.log('Hello from Child'); 11 } 12} 13 14const child = new Child(); 15child.greet(); // prints: Hello from Child

In this script, the Child class overrides the Parent class's greet(). When we invoke greet() on a Child instance, the Child class message is printed, not the Parent's.

It's OOP's flexibility allowing us to tailor classes to meet their intended purpose. Handle it with care — respect the intended use of inherited methods and ensure your updated method is suitable for your class's needs!

Lesson Summary and Practice

Great work! Today, we've covered and understood inheritance in TypeScript, explored how properties and methods traverse from a parent class to a child class via extends, and identified how super initiates a connection between a child class and its parent class.

The next step is effective practice sessions, where you'll create classes related to real-world scenarios. And remember, practice is the key to mastery! Stay motivated, and continue building your coding prowess!

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