TypeScript Inheritance

TypeScript inheritance allows one class to reuse and extend the properties and methods of another class. The existing class is called the base, parent, or superclass. The class that extends it is called the derived, child, or subclass.

A derived class can use accessible members inherited from its base class, add new members, and override inherited methods. TypeScript supports class inheritance through extends and contract inheritance through interfaces.

TypeScript Inheritance Syntax with extends

Place the extends keyword after the child class name, followed by the parent class name:

</>
Copy
class ChildClassName extends ParentClassName{
    // class body
}

The child class inherits accessible instance properties and methods from the parent class. It can then declare additional properties, constructors, accessors, and methods of its own.

TypeScript Inheritance Example with Person and Student

In the following example, Person is the parent class and Student is the child class. A student is modeled as a person with an additional roll number and a method that displays student information.

example.ts

</>
Copy
class Person{
    name:string
    speak():void{
        console.log(this.name+" can speak.")
    }
}
class Student extends Person{
    // variables
    rollnumber:number
    // constructors
    constructor(rollnumber:number, name1:string){
        super(); // calling Parent's constructor
        this.rollnumber = rollnumber
        this.name = name1
    }
    // functions
    displayInformation():void{
        console.log("Name : "+this.name+", Roll Number : "+this.rollnumber)
    }
}
var student1 = new Student(2, "Rohit")
var student2 = new Student(4, "Kohli")
// accessing variables
console.log("Student 1 name is : "+student1.name)
console.log("Student 2 roll number is : "+student2.rollnumber)
console.log("\n---Student 1---")
// calling functions
student1.displayInformation()
// calling funciton of parent class
student1.speak()
console.log("\n---Student 2---")
student2.displayInformation()
student2.speak()

The name property and speak() method are declared in Person. Because they are accessible to the derived class, objects created from Student can use both members. The child class adds rollnumber and displayInformation().

The Student constructor calls super() before accessing this. This invokes the parent constructor, even though the parent class in this example uses an implicit constructor without parameters.

Compile the TypeScript file with tsc example.ts, and then run the generated JavaScript in an appropriate JavaScript environment.

Output

Student 1 name is : Rohit
Student 2 roll number is : 4
 
---Student 1---
Name : Rohit, Roll Number : 2
Rohit can speak.
---Student 2---
Name : Kohli, Roll Number : 4
Rohit can speak.

Based on the source code, the final line produced by student2.speak() is Kohli can speak. because student2.name contains "Kohli".

The exact JavaScript emitted by TypeScript depends on the compiler version and the target setting in tsconfig.json. For an older JavaScript target, the generated file can look like the following:

example.js

</>
Copy
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var Person = /** @class */ (function () {
    function Person() {
    }
    Person.prototype.speak = function () {
        console.log(this.name + " can speak.");
    };
    return Person;
}());
var Student = /** @class */ (function (_super) {
    __extends(Student, _super);
    // constructors
    function Student(rollnumber, name1) {
        var _this = _super.call(this) || this;
        _this.rollnumber = rollnumber;
        _this.name = name1;
        return _this;
    }
    // functions
    Student.prototype.displayInformation = function () {
        console.log("Name : " + this.name + ", Roll Number : " + this.rollnumber);
    };
    return Student;
}(Person));
var student1 = new Student(2, "Rohit");
var student2 = new Student(4, "Kohli");
// accessing variables
console.log("Student 1 name is : " + student1.name);
console.log("Student 2 roll number is : " + student2.rollnumber);
console.log("\n---Student 1---");
// calling functions
student1.displayInformation();
// calling funciton of parent class
student1.speak();
console.log("\n---Student 2---");
student2.displayInformation();
student2.speak();

Calling the Parent Constructor with super()

A child-class constructor uses super() to invoke the parent constructor. When the parent constructor requires arguments, pass them through the super() call.

</>
Copy
class Person {
    constructor(public name: string) {}
}

class Student extends Person {
    constructor(
        name: string,
        public rollNumber: number
    ) {
        super(name);
    }
}

const student = new Student("Asha", 12);
console.log(student.name);
console.log(student.rollNumber);
Asha
12

In a derived constructor, super() must run before the constructor reads or writes this. The parent constructor initializes the inherited name property, while the parameter property initializes rollNumber.

Overriding Inherited Methods in TypeScript

A child class can replace an inherited method with a method of the same name. The overriding method must remain compatible with the method declared by the parent class. The override modifier makes the intent explicit and can be required by enabling noImplicitOverride.

</>
Copy
class Notification {
    send(): string {
        return "Notification sent";
    }
}

class EmailNotification extends Notification {
    override send(): string {
        return "Email notification sent";
    }
}

const notification = new EmailNotification();
console.log(notification.send());
Email notification sent

The method selected at runtime depends on the actual object. An EmailNotification object therefore uses the overridden implementation even when a variable is typed as Notification.

Calling an Inherited Method with super

Inside an overriding instance method, use super.methodName() when the child behavior should build on the parent implementation instead of replacing it completely.

</>
Copy
class Report {
    format(): string {
        return "Report";
    }
}

class SalesReport extends Report {
    override format(): string {
        return `${super.format()}: Sales`;
    }
}

const report = new SalesReport();
console.log(report.format());
Report: Sales

public, protected, and private Members in Inherited Classes

Access modifiers determine which members a derived class can use.

ModifierBase classDerived classCode using an object
publicAccessibleAccessibleAccessible
protectedAccessibleAccessibleNot accessible
privateAccessibleNot directly accessibleNot accessible
</>
Copy
class Account {
    public owner: string;
    protected balance: number;
    private pin: string;

    constructor(owner: string, balance: number, pin: string) {
        this.owner = owner;
        this.balance = balance;
        this.pin = pin;
    }

    protected verifyPin(value: string): boolean {
        return this.pin === value;
    }
}

class SavingsAccount extends Account {
    addInterest(rate: number): void {
        this.balance += this.balance * rate;
    }

    getBalance(): number {
        return this.balance;
    }
}

SavingsAccount can access the protected balance property but cannot directly access the private pin property. Code outside the class hierarchy can access owner because it is public.

Multilevel and Hierarchical Inheritance in TypeScript

TypeScript can represent several inheritance structures with classes:

  • Single inheritance: one child class extends one parent class.
  • Multilevel inheritance: a class extends a parent that already extends another class.
  • Hierarchical inheritance: multiple child classes extend the same parent class.

TypeScript classes do not support multiple class inheritance, so one class cannot directly extend two parent classes. Multiple behavioral contracts can instead be expressed through interfaces, and reusable behavior can often be assembled through composition.

</>
Copy
class LivingThing {
    isAlive: boolean = true;
}

class Animal extends LivingThing {
    move(): string {
        return "Animal moves";
    }
}

class Dog extends Animal {
    bark(): string {
        return "Woof";
    }
}

const dog = new Dog();
console.log(dog.isAlive);
console.log(dog.move());
console.log(dog.bark());

This is multilevel inheritance: Dog inherits directly from Animal and indirectly from LivingThing.

Abstract Class Inheritance in TypeScript

An abstract class provides a shared base for related classes. It can contain initialized properties and implemented methods while leaving selected methods for child classes to implement. An abstract class cannot be instantiated directly.

</>
Copy
abstract class Shape {
    constructor(public color: string) {}

    abstract area(): number;

    describe(): string {
        return `${this.color} shape with area ${this.area()}`;
    }
}

class Rectangle extends Shape {
    constructor(
        color: string,
        private width: number,
        private height: number
    ) {
        super(color);
    }

    area(): number {
        return this.width * this.height;
    }
}

const rectangle = new Rectangle("Blue", 6, 4);
console.log(rectangle.describe());
Blue shape with area 24

Shape supplies the constructor and describe() implementation. Each concrete child class must provide a compatible area() method.

TypeScript Interface Inheritance and Class Implementation

Interfaces use extends to combine type contracts. A class uses implements to declare that its instances satisfy an interface. Implementing an interface does not copy method bodies or initialize properties; the class must supply the required members.

</>
Copy
interface Identifiable {
    id: number;
}

interface Timestamped {
    createdAt: Date;
}

interface RecordItem extends Identifiable, Timestamped {
    describe(): string;
}

class DocumentRecord implements RecordItem {
    constructor(
        public id: number,
        public createdAt: Date,
        public title: string
    ) {}

    describe(): string {
        return `${this.id}: ${this.title}`;
    }
}

An interface can extend multiple interfaces. This provides multiple type contracts without requiring a class to inherit implementation from multiple parent classes.

Implementing a Type Alias in a TypeScript Class

A class can also implement an object-shaped type alias. As with an interface, the type describes the required instance structure rather than supplying an implementation.

</>
Copy
type Loggable = {
    message: string;
    log(): void;
};

class EventLog implements Loggable {
    constructor(public message: string) {}

    log(): void {
        console.log(this.message);
    }
}

The implemented type must describe an object shape that a class instance can satisfy. Union types, such as TypeA | TypeB, cannot be implemented directly because the required member set is not statically fixed.

Why TypeScript Classes Do Not Support Multiple Inheritance

A TypeScript class can extend only one base class. The following form is not valid TypeScript syntax:

</>
Copy
// Invalid: a class cannot extend two classes
class Combined extends FirstBase, SecondBase {
}

When a class needs several capabilities, use multiple interfaces for type contracts or store helper objects and delegate work to them. TypeScript also supports mixin patterns, but composition is usually easier to follow when the relationship is based on capabilities rather than a clear parent-child identity.

Inheritance versus Composition in TypeScript

Inheritance represents an “is-a” relationship: a Student is a Person. Composition represents a “has-a” relationship: a service has a logger. Choose inheritance when the child is a genuine specialization of the parent and can safely be used wherever the parent is expected.

</>
Copy
class Logger {
    log(message: string): void {
        console.log(message);
    }
}

class OrderService {
    constructor(private logger: Logger) {}

    placeOrder(orderId: number): void {
        this.logger.log(`Placed order ${orderId}`);
    }
}

const service = new OrderService(new Logger());
service.placeOrder(105);

OrderService does not need to be a kind of Logger. Holding a logger instance expresses the relationship directly and allows the dependency to be replaced when needed.

TypeScript Inheritance QA Checklist

  • Confirm that every child class represents a valid specialization of its parent class.
  • Verify that each derived constructor calls super() before accessing this.
  • Check that arguments passed to super() match the parent constructor parameters.
  • Use override for methods intended to replace inherited behavior, especially when noImplicitOverride is enabled.
  • Ensure overriding methods remain compatible with the corresponding parent methods.
  • Confirm that derived classes do not directly access private parent members.
  • Use protected only for members that are intentionally part of the subclass API.
  • Check that abstract subclasses implement every inherited abstract member before they are instantiated.
  • Use interfaces or composition when the design requires several independent capabilities rather than one base implementation.
  • Compile examples with the project’s actual tsconfig.json because strictness checks and emitted JavaScript depend on compiler settings.

Frequently Asked Questions about TypeScript Inheritance

Does TypeScript support inheritance?

Yes. A class inherits from another class with extends. Interfaces can also extend other interfaces, and classes can satisfy interface contracts with implements.

What is a basic example of inheritance in TypeScript?

class Student extends Person is a basic inheritance example. Student receives accessible properties and methods from Person and can add student-specific members such as rollNumber.

Does TypeScript support multiple class inheritance?

No. A class can directly extend only one class. It can implement multiple interfaces, and composition or mixin patterns can combine capabilities without multiple base classes.

What is the difference between extends and implements in TypeScript?

A class uses extends to inherit implementation and accessible state from one base class. It uses implements to prove that its instances conform to an interface or compatible object-shaped type; no implementation is inherited from that contract.

Why must a TypeScript child constructor call super()?

super() invokes the parent constructor and initializes the parent portion of the object. In a derived constructor, it must execute before this can be used.

TypeScript Inheritance Summary

TypeScript uses extends for class inheritance and super() to call a parent constructor. Derived classes can add members, override methods, and access public or protected parent members. Abstract classes provide shared implementation with required child behavior, while interfaces define contracts without transferring implementation.

In this TypeScript Tutorial, we have learnt about TypeScript Inheritance, and how this Object Oriented Concept could be used to extend the functionality of a class in another class.