Расширенные дженерики TypeScript с примерами
Generics в TypeScript предоставляют способ создания повторно используемых и гибких компонентов кода, работая с различными типами данных. Расширенные generics развивают эту концепцию дальше, вводя дополнительные функции, такие как ограничения, значения по умолчанию и множественные типы, которые позволяют разработчикам писать более надежный и типобезопасный код. В этой статье будут использоваться примеры для изучения этих расширенных концепций generics.
Общие ограничения
Ограничения ограничивают типы, которые может принять generic. Это гарантирует, что тип, переданный generic-функции или классу, соответствует определенным критериям. Например, ограничение может использоваться для обеспечения того, чтобы generic-тип имел определенное свойство или метод.
function getLength<T extends { length: number }>(arg: T): number {
return arg.length;
}
const stringLength = getLength("TypeScript");
const arrayLength = getLength([1, 2, 3]);
В этом примере ограничение <T extends { length: number }>
гарантирует, что аргумент, переданный в getLength
, имеет свойство length
.
Несколько дженериков
TypeScript позволяет использовать несколько универсальных типов в одной и той же функции, классе или интерфейсе. Это полезно при работе с парами значений или другими структурами данных, включающими несколько типов.
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const stringNumberPair = pair("TypeScript", 2024);
Эта функция, pair
, принимает два различных универсальных типа, T
и U
, и возвращает кортеж, содержащий оба типа.
Стандартные универсальные типы
Generics в TypeScript также могут иметь типы по умолчанию. Это полезно, когда вы хотите, чтобы generic имел резервный тип, если не указан конкретный тип.
function identity<T = string>(value: T): T {
return value;
}
const defaultString = identity("Hello"); // T is string
const customNumber = identity<number>(100); // T is number
В этом примере, если в identity
не передан тип, по умолчанию используется string
.
Использование дженериков с интерфейсами
Generics можно использовать с интерфейсами для определения сложных структур, где типы не фиксированы. Это добавляет гибкости в управление данными.
interface Container<T> {
value: T;
}
const stringContainer: Container<string> = { value: "Hello" };
const numberContainer: Container<number> = { value: 42 };
Интерфейс Container
предназначен для хранения значения любого типа, что позволяет использовать различные виды контейнеров с определенными типами.
Универсальные классы
Классы в TypeScript также могут быть универсальными. Это особенно полезно при проектировании классов, работающих с различными типами данных, например, классы хранения данных или коллекции.
class DataStore<T> {
private data: T[] = [];
add(item: T): void {
this.data.push(item);
}
getAll(): T[] {
return this.data;
}
}
const stringStore = new DataStore<string>();
stringStore.add("Hello");
stringStore.add("TypeScript");
const numberStore = new DataStore<number>();
numberStore.add(42);
В этом примере класс DataStore
работает с любым типом данных, предоставляя типобезопасный способ хранения и извлечения элементов.
Заключение
Расширенные обобщения в TypeScript — это мощный инструмент для написания гибкого, повторно используемого и типобезопасного кода. Используя ограничения, множественные типы, значения по умолчанию и обобщения в классах и интерфейсах, разработчики могут писать более сложный и надежный код. Понимание и использование этих расширенных концепций обеспечивает большую гибкость и безопасность типов в приложениях.