Как сочетаются наследование и дженерики?

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

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

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

Например, представим, что у нас есть абстрактный класс Collection<T>, который представляет коллекцию объектов определенного типа. У этого класса есть методы по добавлению и удалению элементов из коллекции. Также у нас есть класс List<T>, который наследуется от Collection<T>. В классе List<T> мы можем определить дополнительные методы и свойства, специфичные для списка.

abstract class Collection<T> {
  protected items: T[] = [];

  addItem(item: T) {
    this.items.push(item);
  }

  removeItem(item: T) {
    const index = this.items.indexOf(item);
    if (index >= 0) {
      this.items.splice(index, 1);
    }
  }
}

class List<T> extends Collection<T> {
  getItem(index: number): T {
    return this.items[index];
  }

  getLength(): number {
    return this.items.length;
  }
}

Теперь мы можем создать экземпляр класса List<string> и использовать его методы, чтобы добавлять и удалять строки из списка:

const stringList = new List<string>();
stringList.addItem("Hello");
stringList.addItem("World");
console.log(stringList.getLength()); // Output: 2
console.log(stringList.getItem(0)); // Output: "Hello"

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