- TypeScript Tutorial
- TypeScript Home
- TypeScript Introduction
- TypeScript Setup
- TypeScript First Program
- TypeScript vs JavaScript
- TypeScript Data Types
- TypeScript Type Inference
- TypeScript Type Annotations
- TypeScript Interfaces
- TypeScript Enums
- TypeScript Type Aliases
- TypeScript Type Assertions
- TypeScript Variables
- TypeScript Functions
- TypeScript Functions
- TypeScript Optional Parameters
- TypeScript Default Parameters
- TypeScript Rest Parameters
- TypeScript Arrow Functions
- Classes and Objects
- Introduction to Classes
- Properties and Methods
- Access Modifiers
- Static Members
- Inheritance
- Abstract Classes
- Interfaces vs Classes
- Advanced Types
- TypeScript Union Types
- TypeScript Intersection Types
- TypeScript Literal Types
- TypeScript Nullable Types
- TypeScript Type Guards
- TypeScript Discriminated Unions
- TypeScript Index Signatures
- TypeScript Generics
- Introduction to Generics
- TypeScript Generic Functions
- TypeScript Generic Classes
- TypeScript Generic Constraints
- TypeScript Modules
- Introduction to Modules
- TypeScript Import and Export
- TypeScript Default Exports
- TypeScript Namespace
- Decorators
- Introduction to Decorators
- TypeScript Class Decorators
- TypeScript Method Decorators
- TypeScript Property Decorators
- TypeScript Parameter Decorators
- Configuration
- TypeScript tsconfig.json File
- TypeScript Compiler Options
- TypeScript Strict Mode
- TypeScript Watch Mode
Inheritance in TypeScript
Inheritance is a fundamental pillar of object-oriented programming (OOP). It allows you to create a blueprint (a class) that "inherits" the characteristics and behaviors of another blueprint. This creates a natural hierarchy in your code, similar to how a "Car" is a specific type of "Vehicle." In TypeScript, we use inheritance to promote the DRY (Don't Repeat Yourself) principle, allowing us to reuse code across multiple classes without rewriting the same logic over and over.
Manager is a Employee. If you find yourself using inheritance just to share a few utility functions, consider using composition or utility modules instead.
Key Points About Inheritance:
- Base class (superclass): Think of this as the "parent" or generic class. It contains the common logic that multiple other classes will share.
- Derived class (subclass): This is the "child" class. It inherits everything from the base class but can also have its own unique features.
- Extensibility: A derived class isn't just a copy; it can add new properties, introduce new methods, or "override" (change) how a parent method works.
Inheriting from a Class
To implement inheritance in TypeScript, we use the extends keyword. When one class extends another, it gains access to all the non-private members of the parent. This is incredibly useful for building complex systems where different objects share a common foundation.
Example:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
speak(): void {
console.log(`${this.name} makes a sound.`);
}
}
class Dog extends Animal {
breed: string;
constructor(name: string, breed: string) {
// We must call the parent constructor using super()
super(name);
this.breed = breed;
}
speak(): void {
console.log(`${this.name} barks!`);
}
}
const dog = new Dog("Buddy", "Golden Retriever");
dog.speak(); // Output: Buddy barks!
In this example:
- The
Dogclass extendsAnimal. This means everyDogautomatically has anameproperty and aspeakmethod. - We added a unique property
breedthat only exists onDog, not on the generalAnimalclass. - By defining
speak()insideDog, we specialized the behavior for dogs specifically.
Button or Slider component inherits from a base UIElement class to share logic like positioning and visibility.
The super Keyword
The super keyword is your bridge back to the parent class. In a derived class, super serves two main purposes: calling the parent's constructor and calling the parent's methods. If your child class has its own constructor, you must call super() before you try to use this.
Example:
class Animal {
constructor(public name: string) {}
speak() {
console.log(`${this.name} makes a sound.`);
}
}
class Cat extends Animal {
constructor(name: string) {
super(name); // Passes the name up to the Animal constructor
}
speak() {
console.log(`${this.name} meows.`);
}
}
const cat = new Cat("Whiskers");
cat.speak(); // Output: Whiskers meows.
super() before accessing any properties with this. If you forget, TypeScript will throw a compiler error.
Access Modifiers and Inheritance
Control over who can see your data is vital in large applications. TypeScript provides three main access modifiers that behave differently during inheritance:
- public: The default. Accessible from anywhere inside the class, subclasses, and external code.
- protected: Only accessible within the class itself and its subclasses. This is perfect for "internal" logic you want children to use, but want to hide from the rest of the world.
- private: Only accessible within the specific class where it was defined. Subclasses cannot see private members of their parents.
Example:
class Employee {
public name: string;
protected salary: number;
private id: number;
constructor(name: string, salary: number, id: number) {
this.name = name;
this.salary = salary;
this.id = id;
}
}
class Manager extends Employee {
public getDetails() {
// This works! Subclasses can access protected members.
return `${this.name} earns ${this.salary}.`;
}
public getId() {
// ERROR: Property 'id' is private and only accessible within class 'Employee'.
// return this.id;
}
}
private when they actually need protected. If you plan on extending a class and need the child to use a specific property, make it protected.
Method Overriding
Method overriding allows a subclass to provide a specific implementation of a method that is already provided by its parent. This is how we achieve polymorphism the ability for different types to be treated as their parent type while still maintaining their unique behaviors.
Example:
class Report {
generate(): void {
console.log("Generating a generic report...");
}
}
class FinancialReport extends Report {
generate(): void {
console.log("Generating financial charts and balance sheets...");
}
}
class PerformanceReport extends Report {
generate(): void {
super.generate(); // We can call the parent's version too!
console.log("Adding employee performance metrics...");
}
}
const reports: Report[] = [new FinancialReport(), new PerformanceReport()];
reports.forEach(r => r.generate());
In this example, PerformanceReport uses super.generate(). This is a common pattern where you want to *add* to the parent's behavior rather than completely replacing it.
Summary
Inheritance is a powerful tool in TypeScript that helps you organize your code into logical hierarchies. By using the extends keyword, you can build upon existing logic, reducing duplication and making your codebase easier to maintain.
- The
extendskeyword: Establishes the link between a base and derived class. - The
superkeyword: Necessary for initializing the parent class and accessing its methods. - Access modifiers:
protectedis your best friend when you want to share data with children but hide it from the public API. - Polymorphism: Overriding methods allows different subclasses to respond to the same method call in their own unique way.
Mastering inheritance will allow you to design more robust, scalable TypeScript applications by creating reusable "building blocks" of logic.