Введение

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

В этой статье мы подробно рассмотрим дженерики в TypeScript, предоставив информацию об их реализации и полезности с несколькими иллюстративными примерами.

Общие сведения

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

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

function identity<T>(arg: T): T {
    return arg;
}

let output = identity<string>("myString");

В приведенном выше примере <T> — это переменная типа — своего рода переменная, которая работает с типами, а не со значениями.

Общие переменные

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

function identity<T>(arg: T): T {
    console.log(arg.length);  // Error: T doesn't necessarily have a .length
    return arg;
}ty

В приведенном выше примере вы получите сообщение об ошибке, поскольку не все типы имеют свойство .length. Чтобы это работало, мы можем указать, что T должно быть ограничено типами, у которых есть свойство .length.

interface Lengthy {
    length: number;
}

function loggingIdentity<T extends Lengthy>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}

loggingIdentity("hello world");  // OK
loggingIdentity(3);  // Error: number doesn't have a .length property

Общие классы

Универсальный класс — это класс с формой, аналогичной универсальному интерфейсу. Универсальные классы имеют список параметров универсального типа в угловых скобках (<>) после имени класса.

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

Общие ограничения

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

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length); // OK, because T extends Lengthwise
    return arg;
}

Заключение

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

Помните, что практика является ключом к освоению дженериков. Итак, возьмите приведенные выше примеры, настройте их, сломайте и исправьте снова, чтобы узнать больше о дженериках.

Удачного кодирования!