логотип PurpleSchool
Иконка входа
Вход
логотип PurpleSchool

Наследование в TypeScript

Автор

Дмитрий Нечаев

Наследование является одним из ключевых принципов объектно-ориентированного программирования (ООП), и TypeScript, как строго типизированный язык, поддерживает его на высоком уровне. Наследование позволяет создавать новые классы на основе уже существующих, что способствует повторному использованию кода и упрощает его сопровождение. В TypeScript наследование реализуется с помощью ключевого слова extends.

Основы наследования

Когда один класс наследует другой, он приобретает все свойства и методы родительского класса, при этом может добавлять свои собственные или переопределять унаследованные. Класс, который наследует другой класс, называется дочерним или производным классом, а класс, от которого наследуются, называется родительским или базовым классом.

Пример базового и дочернего классов

class Animal {
    name: string;

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

    makeSound(): void {
        console.log(`${this.name} makes a sound.`);
    }
}

class Dog extends Animal {
    constructor(name: string) {
        super(name); // Вызов конструктора базового класса
    }

    makeSound(): void {
        console.log(`${this.name} barks.`);
    }
}

let dog = new Dog("Rex");
dog.makeSound(); // "Rex barks."

Конструкторы и наследование

Когда дочерний класс имеет собственный конструктор, он должен вызывать конструктор базового класса с помощью функции super. Это необходимо для инициализации свойств, объявленных в базовом классе.

Пример с конструкторами

class Vehicle {
    make: string;
    model: string;

    constructor(make: string, model: string) {
        this.make = make;
        this.model = model;
    }

    displayInfo(): void {
        console.log(`Vehicle: ${this.make} ${this.model}`);
    }
}

class Car extends Vehicle {
    year: number;

    constructor(make: string, model: string, year: number) {
        super(make, model); // Вызов конструктора базового класса
        this.year = year;
    }

    displayInfo(): void {
        super.displayInfo(); // Вызов метода базового класса
        console.log(`Year: ${this.year}`);
    }
}

let car = new Car("Toyota", "Camry", 2020);
car.displayInfo();
// "Vehicle: Toyota Camry"
// "Year: 2020"

Переопределение методов

Дочерний класс может переопределять методы базового класса, предоставляя свою реализацию. Для вызова метода базового класса из переопределенного метода используется ключевое слово super.

Пример переопределения методов

class Bird {
    name: string;

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

    fly(): void {
        console.log(`${this.name} is flying.`);
    }
}

class Eagle extends Bird {
    fly(): void {
        console.log(`${this.name} is soaring high.`);
    }
}

let eagle = new Eagle("Eagle");
eagle.fly(); // "Eagle is soaring high."

Модификаторы доступа и наследование

TypeScript поддерживает модификаторы доступа public, private и protected, которые контролируют доступ к свойствам и методам класса. Модификатор protected позволяет свойствам и методам быть доступными в дочерних классах, но не снаружи этих классов.

Пример использования protected

class Person {
    protected name: string;

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

class Employee extends Person {
    private salary: number;

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

    displayInfo(): void {
        console.log(`Employee: ${this.name}, Salary: ${this.salary}`);
    }
}

let employee = new Employee("Alice", 50000);
employee.displayInfo(); // "Employee: Alice, Salary: 50000"
// console.log(employee.name); // Ошибка: свойство 'name' защищено

Абстрактные классы и методы

Абстрактные классы используются для создания базовых классов, которые не могут быть инстанцированы напрямую. Абстрактные методы в таких классах должны быть реализованы в дочерних классах.

Пример абстрактного класса

abstract class Shape {
    abstract area(): number;

    describe(): void {
        console.log(`This shape has an area of ${this.area()} square units.`);
    }
}

class Circle extends Shape {
    radius: number;

    constructor(radius: number) {
        super();
        this.radius = radius;
    }

    area(): number {
        return Math.PI * this.radius ** 2;
    }
}

let circle = new Circle(5);
circle.describe(); // "This shape has an area of 78.53981633974483 square units."

Интерфейсы и наследование

Классы могут реализовывать интерфейсы, обеспечивая наличие определенных методов и свойств. Классы могут также наследовать другие классы и одновременно реализовывать интерфейсы.

Пример реализации интерфейса

interface Movable {
    move(): void;
}

class Vehicle implements Movable {
    move(): void {
        console.log("Vehicle is moving.");
    }
}

class Bike extends Vehicle {
    move(): void {
        console.log("Bike is moving.");
    }
}

let bike = new Bike();
bike.move(); // "Bike is moving."

Множественное наследование интерфейсов

TypeScript поддерживает множественное наследование интерфейсов, что позволяет классу реализовывать несколько интерфейсов.

Пример множественного наследования интерфейсов

interface Flyable {
    fly(): void;
}

interface Swimmable {
    swim(): void;
}

class Duck implements Flyable, Swimmable {
    fly(): void {
        console.log("Duck is flying.");
    }

    swim(): void {
        console.log("Duck is swimming.");
    }
}

let duck = new Duck();
duck.fly(); // "Duck is flying."
duck.swim(); // "Duck is swimming."

Заключение

Наследование в TypeScript предоставляет мощные инструменты для создания иерархий классов и повторного использования кода. Оно позволяет строить сложные системы, разделяя функциональность между различными уровнями абстракции. Модификаторы доступа, абстрактные классы и интерфейсы добавляют гибкость и строгость в управление доступом и реализацией функциональности. Использование наследования помогает сделать код более организованным, читаемым и легко поддерживаемым.

Карта развития разработчика

Получите полную карту развития разработчика по всем направлениям: frontend, backend, devops, mobile