Базовые дженерики TypeScript: практическое руководство по повышению возможности повторного использования кода

Вы устали писать повторяющийся код на TypeScript? Хотите повысить возможность повторного использования вашего кода, обеспечив при этом безопасность типов? Не смотрите дальше! В этой статье мы погрузимся в мир дженериков TypeScript. Обобщения позволяют нам писать гибкий и многократно используемый код, который работает с разными типами данных, не жертвуя безопасностью типов. Итак, давайте начнем и рассмотрим некоторые важные методы и приемы использования дженериков в TypeScript.

  1. Общие функции:

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

function reverseArray<T>(array: T[]): T[] {
  return array.reverse();
}
const numbers = [1, 2, 3, 4, 5];
const reversedNumbers = reverseArray(numbers); // [5, 4, 3, 2, 1]
const names = ["Alice", "Bob", "Charlie"];
const reversedNames = reverseArray(names); // ["Charlie", "Bob", "Alice"]
  1. Общие классы:

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

class Stack<T> {
  private elements: T[] = [];
  push(element: T): void {
    this.elements.push(element);
  }
  pop(): T | undefined {
    return this.elements.pop();
  }
}
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
numberStack.push(3);
console.log(numberStack.pop()); // 3
const stringStack = new Stack<string>();
stringStack.push("Hello");
stringStack.push("World");
console.log(stringStack.pop()); // "World"
  1. Общие интерфейсы:

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

interface Comparable<T> {
  compareTo(other: T): number;
}
class Person implements Comparable<Person> {
  constructor(public name: string) {}
  compareTo(other: Person): number {
    return this.name.localeCompare(other.name);
  }
}
const alice = new Person("Alice");
const bob = new Person("Bob");
console.log(alice.compareTo(bob)); // -1
console.log(bob.compareTo(alice)); // 1
  1. Ограничения в универсальных шаблонах:

Иногда мы хотим ограничить типы, которые можно использовать с дженериками. Мы можем добиться этого, используя ограничения. Вот пример, демонстрирующий ограничение универсального типа объектом с определенным свойством:

function printProperty<T extends { name: string }>(obj: T): void {
  console.log(obj.name);
}
const person = { name: "Alice", age: 25 };
const book = { title: "Harry Potter", author: "J.K. Rowling" };
printProperty(person); // "Alice"
printProperty(book); // Compilation error: Property 'name' is missing

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