Article image Classes and Inheritance in TypeScript

13. Classes and Inheritance in TypeScript

Page 13 | Listen in audio

In TypeScript, classes and inheritance play a crucial role in building robust and reusable code, allowing developers to create complex systems with a clear and maintainable structure. TypeScript, being a superset of JavaScript, brings the power of static typing to JavaScript's prototype-based inheritance model, enabling developers to leverage object-oriented programming (OOP) paradigms more effectively.

At its core, a class in TypeScript is a blueprint for creating objects. It encapsulates data for the object and methods to manipulate that data. TypeScript enhances JavaScript classes by adding type annotations and access modifiers, which help in catching errors early during development and enforcing encapsulation.

Defining Classes

To define a class in TypeScript, you use the class keyword followed by the class name. Inside the class, you can define properties and methods. Here's a simple example:

class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    move(distance: number = 0) {
        console.log(`${this.name} moved ${distance}m.`);
    }
}

In this example, we have a class Animal with a property name and a constructor that initializes this property. The class also has a method move which logs the movement of the animal.

Access Modifiers

TypeScript provides three access modifiers to control the visibility of class members: public, private, and protected.

  • Public: By default, all members of a class are public, meaning they can be accessed from anywhere.
  • Private: Members marked as private can only be accessed within the class they are declared.
  • Protected: Similar to private, but members declared as protected can also be accessed within subclasses.

Here's an example demonstrating these modifiers:

class Animal {
    private name: string;
    protected age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    public move(distance: number = 0) {
        console.log(`${this.name} moved ${distance}m.`);
    }
}

In this example, the name property is private, and the age property is protected. This encapsulation ensures that sensitive data is not accessible from outside the class or its subclasses.

Inheritance

Inheritance is a fundamental concept in OOP that allows a class to inherit properties and methods from another class. In TypeScript, you use the extends keyword to create a subclass. Here's how you can extend the Animal class:

class Bird extends Animal {
    fly(distance: number) {
        console.log(`${this.name} flew ${distance}m.`);
    }
}

In this example, Bird is a subclass of Animal. It inherits the properties and methods of Animal and adds a new method fly.

Method Overriding

Subclasses can override methods from their parent class to provide specific behavior. To override a method, you simply define a method in the subclass with the same name as the method in the parent class. Here's an example:

class Snake extends Animal {
    move(distance: number = 5) {
        console.log("Slithering...");
        super.move(distance);
    }
}

In this example, the move method in the Snake class overrides the move method in the Animal class. The super keyword is used to call the parent class's method.

Abstract Classes

TypeScript allows you to define abstract classes, which cannot be instantiated directly. Abstract classes are used as base classes from which other classes can derive. They can contain implementation details and abstract methods that must be implemented by subclasses.

abstract class Department {
    constructor(public name: string) {}

    printName(): void {
        console.log("Department name: " + this.name);
    }

    abstract printMeeting(): void; // must be implemented in derived classes
}

In this example, Department is an abstract class with an implemented method printName and an abstract method printMeeting. Any subclass of Department must implement the printMeeting method.

Interfaces and Classes

In TypeScript, interfaces can be used to define the shape of an object, and classes can implement interfaces. This allows for a contract that a class must adhere to. Here's an example:

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date): void;
}

class Clock implements ClockInterface {
    currentTime: Date;

    constructor(h: number, m: number) {
        this.currentTime = new Date();
    }

    setTime(d: Date) {
        this.currentTime = d;
    }
}

In this example, the Clock class implements the ClockInterface interface, ensuring that it provides a definition for the currentTime property and the setTime method.

Mixins

TypeScript supports mixins, a pattern that allows classes to be composed from reusable components. Mixins are useful when you want to share functionality between classes without using inheritance. Here's a simple example:

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        });
    });
}

class Disposable {
    isDisposed: boolean = false;
    dispose() {
        this.isDisposed = true;
    }
}

class Activatable {
    isActive: boolean = false;
    activate() {
        this.isActive = true;
    }
    deactivate() {
        this.isActive = false;
    }
}

class SmartObject implements Disposable, Activatable {
    isDisposed: boolean = false;
    dispose: () => void;
    isActive: boolean = false;
    activate: () => void;
    deactivate: () => void;

    constructor() {
        setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
    }
}

applyMixins(SmartObject, [Disposable, Activatable]);

In this example, the SmartObject class uses mixins to combine the functionality of the Disposable and Activatable classes.

Conclusion

Classes and inheritance in TypeScript provide a structured way to define and organize code, enabling developers to build scalable and maintainable applications. By leveraging TypeScript's type system, access modifiers, and features like abstract classes and interfaces, developers can create robust object-oriented designs that are both flexible and type-safe. Understanding these concepts is fundamental to mastering TypeScript and harnessing its full potential to improve JavaScript development.

Now answer the exercise about the content:

What is the role of TypeScript's access modifiers in class design?

You are right! Congratulations, now go to the next page

You missed! Try again.

Article image Understanding Access Modifiers

Next page of the Free Ebook:

14Understanding Access Modifiers

7 minutes

Earn your Certificate for this Course for Free! by downloading the Cursa app and reading the ebook there. Available on Google Play or App Store!

Get it on Google Play Get it on App Store

+ 6.5 million
students

Free and Valid
Certificate with QR Code

48 thousand free
exercises

4.8/5 rating in
app stores

Free courses in
video, audio and text