TypeScript Генерики: Шлях до Розумнішого Коду

typescript
TypeScript, відомий як супerset JavaScript, був створений для подолання деяких обмежень JavaScript.
Під час розробки масштабних додатків динамічно типізована мова JavaScript часто призводить до помилок через відсутність перевірок типів під час виконання. З іншого боку, TypeScript — це статично типізована мова, яка покращує безпеку типів і дозволяє виявляти помилки на етапі розробки, економлячи час на налагодження пізніше.

TypeScript побудований поверх JavaScript і надає додаткові потужні інструменти. Ця стаття присвячена одному з таких важливих інструментів — Generic (генерикам). Вони вирішують проблему відсутності безпеки типів та багаторазового використання функцій і класів, що працюють з кількома типами даних. Генерики є універсальними шаблонами, що дозволяють створювати типобезпечні проекти, які адаптуються до різноманітних типів даних, замість одного конкретного типу. Це дає змогу розробникам повторно використовувати код для різних типів даних, зберігаючи при цьому строгі перевірки типів, що гарантують відповідність даних очікуваній структурі.

Ось приклад функції з використанням генераків:

function addInputs(param1:T,param2:T):T{  
// Якщо обидва значення є рядками, з'єднати їх
if(typeof param1==='string' && typeof param2 ==='string'){  
return (param1 + param2) as T;  
}  
// Якщо обидва значення є числами, додати їх
if(typeof param1==='number' && typeof param2==='number'){  
return (param1 + param2) as T;  
}  
// Якщо типи не збігаються, повернути значення за замовчуванням
return ("Cannot be Added") as T;  
}  
console.log(addInputs(8,2)) // Вивід: 6  
console.log(addInputs("Hello","World")) //Вивід: HelloWorld

У наведеному прикладі функція addInputs(), використовуючи генераки, приймає і повертає різні типи даних, представлені T, роблячи її багаторазовою.

Ось приклад функції з використанням генераків, яка приймає масив будь-яких типів даних як параметр і повертає значення типу number.

function sizeOfArray(param: Array[T]): T{  
return param.length;  
}  
console.log(sizeOfArray([2,8,20,15,4,16,12,6]));  
console.log(sizeOfArray(['H','E','L','L','O']));

Що таке інтерфейс TypeScript?

Інтерфейс TypeScript визначає очікувану структуру об'єкта, вказуючи типи його властивостей та сигнатури методів. Він гарантує, що об'єкти відповідають певній формі. Це сприяє сильним перевіркам типів, покращує чіткість коду і підтримує такі функції, як необов'язкові та тільки для читання властивості, а також можливість розширення інших інтерфейсів для більш складних дизайнів.

Синтаксис генераків в інтерфейсах:

interface Ranking{  
first: T,  
second: U  
}  
const numberRanking: Ranking={  
first: 1,  
second: 2  
}  
const stringRanking: Ranking={  
first: "1st",  
second:"2nd"  
}

Що таке клас TypeScript?

Класи в TypeScript слугують шаблонами для створення багаторазових об'єктів, поєднуючи властивості для зберігання даних і методи для визначення поведінки. Розширюючи класову систему JavaScript, TypeScript вводить вдосконалені функції, такі як безпека типів та контроль доступу через модифікатори (public, private і protected), що сприяє створенню більш надійних і підтримуваних об'єктно-орієнтованих дизайнів.

Синтаксис генераків в класах:

class database{  
 private data: T;  
 constructor(value: T){  
 this.data=value;  
 }  
 public getData():T{  
 return this.data;  
 }  
 public setData(value: T):void{  
 this.data= value;  
 }  
}  
let dbObj1= new database("First Object")  
console.log(dbObj1.getData()); // Вивід: "First Object"  
let dbObj2= new database(123);   
console.log(dbObj2.getData()); // Вивід: 123

Типовий параметр (як T) застосовується лише до екземплярів класу, тобто об'єктів, створених з цього класу, а не до самого класу. Тому, коли ми створюємо статичні члени в класі, вони не можуть використовувати типовий параметр.
Причина цього полягає в тому, що статичні члени спільні для всіх екземплярів (об'єктів) класу і не знають про конкретні типи даних, які використовуються в будь-якому конкретному екземплярі. Тому вони не можуть залежати від параметра типу. Екземпляри (об'єкти), з іншого боку, надають тип для параметра типу генерика під час його створення.

Більше про Генерики...

До генераків можна додавати обмеження для конкретних вимог. Наприклад, якщо ми хочемо використовувати властивість "size" (розмір) параметра.

function calculateLength(param:T):T{  
let length= param.size;  
return length;  
}

У наведеному фрагменті коду компілятор виведе помилку, якщо ми спробуємо отримати доступ до властивості "size" параметра, у якого немає цієї властивості. Щоб вирішити цю проблему, ми повинні додати обмеження до генераків. Це можна зробити, використовуючи інтерфейс, який буде розширюватися функцією “calculateLength()”. Ось правильні рядки коду:

interface Measurable{  
size: number;  
}  
function calculateLength(param:T):T{  
let length= param.size;  
return length;  
}

З цим обмеженням функція відкидатиме аргументи, які не відповідають вимогам:

calculateLength(23);  
// Помилка: Аргумент типу 'number' не можна призначити параметру   
// Помилка: типу 'Measurable'.
calculateLength({ size: 1, value: 3});

Ця техніка забезпечує гнучкість ваших генераків, одночасно дотримуючись специфічних структурних вимог, покращуючи як безпеку типів, так і чіткість коду.

Перекладено з: TypeScript Generics Explained: A Path to Smarter Code

Leave a Reply

Your email address will not be published. Required fields are marked *