Написання коду, який працює з різними типами даних, часто призводить до копіювання та вставки або використання `any`, що знижує безпеку та ускладнює підтримку. Generics у TypeScript дозволяють створювати компоненти, які працюють з широким спектром типів, зберігаючи при цьому типову безпеку. Це особливо корисно при роботі з колекціями, функціями та структурами даних, де потрібно забезпечити коректність типів.
Контекст і чому це важливо
Generics стають критично важливими при розробці бібліотек та компонентів, які мають бути використані в різних проєктах з різними типами даних. Без них доводиться писати багато однакового коду для різних типів, що збільшує обсяг коду та ймовірність помилок. Наприклад, у проєкті електронної комерції вам може знадобитися функція, яка сортує товари за різними властивостями (ціна, назва, дата створення).
Ігнорування generics може призвести до помилок під час виконання, які важко відстежити, особливо у великих проєктах. Замість того, щоб отримувати інформативні повідомлення про помилки типів на етапі розробки, ви отримаєте неочікувані збої в продакшені, що призведе до втрати продуктивності та репутації.
Практична реалізація
Створення узагальненої функції для обробки масивів – типовий приклад використання generics. Ми створимо функцію `processArray`, яка приймає масив будь-якого типу та застосовує до кожного елемента задану функцію.
function processArray<T>(arr: T[], callback: (item: T) => void): void {
for (const item of arr) {
callback(item);
}
}
// Приклад використання з масивом чисел
const numbers: number[] = [1, 2, 3, 4, 5];
processArray(numbers, (num) => {
console.log(num * 2);
});
// Приклад використання з масивом рядків
const strings: string[] = ["hello", "world"];
processArray(strings, (str) => {
console.log(str.toUpperCase());
});
Функція `processArray` використовує параметр типу `T`, який представляє тип елементів у масиві. Це дозволяє функції працювати з масивами чисел, рядків, об’єктів або будь-якого іншого типу даних, зберігаючи при цьому типову безпеку. Функція `callback` приймає елемент масиву типу `T` і виконує над ним задану операцію.
Поширені помилки та підводні камені
- Неправильне вказування типу `T`: Якщо тип `T` не вказаний правильно, TypeScript не зможе перевірити типи правильно, що може призвести до помилок під час виконання. Наприклад, `processArray([1, “hello”])` без вказання типу `T` може призвести до неочікуваної поведінки.
- Надмірне використання `any` всередині generic-ів: Використання `any` в функціях, які використовують generics, нівелює переваги типової безпеки. Завжди намагайтеся визначати конкретні типи.
- Складні обмеження на тип `T`: Занадто складні обмеження на тип `T` можуть ускладнити використання вашого компонента. Старайтеся використовувати максимально прості та зрозумілі обмеження. Наприклад, обмеження типу `T` лише типом `string | number` може обмежувати використання.
Порівняння підходів
Без generics, для сортування масивів різних типів, довелося б писати окремі функції для кожного типу, що призвело б до дублювання коду. Наприклад, для масиву чисел потрібно писати `sortNumbers`, для масиву рядків – `sortStrings`, і так далі. Це збільшило б обсяг коду на 50-70% та ускладнило його підтримку.
За допомогою generics, ми можемо створити одну узагальнену функцію `sortArray
Висновки
Generics — це потужний інструмент у TypeScript для написання гнучкого та безпечного коду. Застосовуйте їх у випадках, коли потрібно працювати з різними типами даних. Почніть з написання простих узагальнених функцій, а потім поступово переходьте до більш складних сценаріїв. Завжди перевіряйте типи, щоб уникнути помилок під час виконання.