Опановуємо TypeScript: Глибоке занурення в типи, інтерфейси, перелічення та генерики

pic

Вступ

TypeScript — чудовий інструмент для розробників JavaScript, оскільки він додає типову безпеку та сучасні функції. Ця стаття охоплює чотири ключові теми в TypeScript: Типи, Інтерфейси, Перелічення (Enums) та Генерики.

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

Основи TypeScript

Що таке TypeScript?

TypeScript — це надмножина JavaScript, яка додає статичну типізацію. Він допомагає розробникам знаходити помилки на ранніх етапах, забезпечує кращі інструменти та покращує підтримуваність коду. На відміну від JavaScript, де типи виводяться або динамічно визначаються, в TypeScript типи можуть бути явно визначені.

Налаштування TypeScript

Щоб почати працювати з TypeScript, ви можете встановити його за допомогою npm:

npm install -g typescript

Щоб скомпілювати ваші .ts файли, запустіть:

npx tsc filename.ts

Типи в TypeScript

pic

Що таке типи?

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

Оголошення типів

Ви можете визначати типи для примітивних значень, масивів та інших типів:

let age: number = 30;  
let isActive: boolean = true;  
let name: string = "Ram";  

let hobbies: string[] = ["Coding", "Reading"];

Об'єднання та перетин типів

Об'єднання типів дозволяє змінній містити значення різних типів:

let value: string | number = "Hello";  
value = 42;

Перетин типів поєднує кілька типів в один:

interface Person {  
 name: string;  
 age: number;  
}  

interface Contact {  
 phone: string;  
 email: string;  
}  

type PersonWithContact = Person & Contact;  

const user: PersonWithContact = {  
 name: "Ram",  
 age: 30,  
 phone: "+977-9804264091",  
 email: "[email protected]"  
};

Кортежі в TypeScript

pic

Кортеж — це масив фіксованого розміру, де типи елементів відомі і можуть змінюватися. Кортежі дозволяють представляти впорядковану колекцію елементів різних типів.
Довжина кортежу є фіксованою, і кожен елемент може мати конкретний тип.

Базовий кортеж

Простий кортеж може містити елементи різних типів:

type Point = [number, number]; // Оголошення типу кортежу  

const point: Point = [10, 20]; // Правильно  
console.log(point[0]); // 10  
console.log(point[1]); // 20

Іменовані кортежі

Ви можете дати описові назви елементам кортежу за допомогою модифікатора readonly та as const для кращої читабельності, хоча це більш просунута функція.

type Coordinate = [x: number, y: number];  

const point: Coordinate = [10, 20];

Необов'язкові елементи кортежу

Ви можете зробити певні елементи кортежу необов'язковими:

type Person = [string, number?, string?]; // Необов'язкові елементи  

const person1: Person = ["Ram", 30]; // Дійсно  
const person2: Person = ["Sita", 25, "Engineer"]; // Дійсно

Параметри решти в кортежах

Ви можете використовувати функцію параметрів решти для представлення невідомої кількості елементів у кортежі:

type LogEntry = [string, ...number[]]; // Перший елемент — це рядок, а решта — числа  

const entry: LogEntry = ["Log started", 1, 2, 3, 4];  
console.log(entry); // ["Log started", 1, 2, 3, 4]

Кортежі з деструктуризацією

Ви можете здійснювати деструктуризацію кортежу, як масиву:

const coordinates: [number, number] = [10, 20];  
const [x, y] = coordinates;  

console.log(x); // 10  
console.log(y); // 20

Інтерфейси в TypeScript

pic

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

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

Оголошення інтерфейсів

Інтерфейси зазвичай використовуються для опису об'єктів:

interface User {  
 id: number;  
 name: string;  
 email: string;  
}  

const user: User = {  
 id: 1,  
 name: "Ram",  
 email: "[email protected]"  
};

Розширення інтерфейсів

Інтерфейси можуть розширювати інші інтерфейси, щоб повторно використовувати та доповнювати їх властивості:

interface User {  
 id: number;  
 name: string;  
 email: string;  
}  

interface Address {  
 street: string;  
 city: string;  
}  

interface UserWithAddress extends User, Address {  
 country: string;  
}  

const userWithAddress: UserWithAddress = {  
 id: 1,  
 name: "Ram",  
 email: "[email protected]",  
 street: "123 Main St",  
 city: "Dreamland",  
 country: "Nepal"  
};

Різниця між типами та інтерфейсами в TypeScript

pic

У TypeScript як types, так і interfaces використовуються для визначення форми об'єкта або підпису функції, але вони мають тонкі відмінності в своїх можливостях та випадках використання. Ось розподіл їхніх відмінностей і коли слід використовувати кожен з них:

1. Відмінності в синтаксисі

  • Інтерфейс: Інтерфейси оголошуються за допомогою ключового слова interface.
interface User {  
 id: number;  
 name: string;  
}
  • Типове псевдонім: Типові псевдоніми оголошуються за допомогою ключового слова type. Типові псевдоніми можуть визначати не лише форми об'єктів, але й примітивні типи, об'єднання, перетини та кортежі.
type User = {  
 id: number;  
 name: string;  
};

2. Розширюваність

  • Інтерфейси можна розширювати (зливати) через різні оголошення:
interface User {  
 id: number;  
}  

interface User {  
 name: string;  
}  

const user: User = { id: 1, name: "Ram" }; // Злиття
  • Типи не можна зливати, але можна використовувати перетин (&) для їх поєднання:
type User = {  
 id: number;  
};  

type ExtendedUser = User & {  
 name: string;  
};  

const user: ExtendedUser = { id: 1, name: "Ram" };

## Зручність використання

- **Інтерфейси** обмежені об'єктами, класами та функціями:

interface Person {
name: string;
age: number;
}
```

  • Типи є більш універсальними і можуть представляти примітиви, об'єднання, кортежі тощо.
type ID = string | number; // Об'єднання  
type Point = [number, string, boolean]; // Кортеж

4. Продуктивність

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

5. Класи

  • Інтерфейси можуть бути реалізовані класами:
interface Printable {  
 print(): void;  
}  

class Document implements Printable {  
 print() {  
 console.log("Printing...");  
 }  
}
  • Типи не можуть бути реалізовані класами безпосередньо:
type Printable = {  
 print: () => void;  
};  

// Помилка: 'type' не може бути реалізований  
// class Document implements Printable {}

6. Перехрестя

Як type, так і interface можуть описувати форму об'єктів, тому вони взаємозамінні в багатьох випадках. Приклад:

interface Car {  
 brand: string;  
 year: number;  
}  

type Car = {  
 brand: string;  
 year: number;  
};

Коли що використовувати

Використовуйте interface:

  • Коли визначаєте об'єкти, функції чи класи, особливо якщо потрібно розширювати або об'єднувати.
  • Для структур відповідей API та багаторазових компонентів.

Використовуйте type:

  • Коли визначаєте об'єднання, перетини, кортежі чи примітиви.
  • Для більш складних типів або коли вам потрібна гнучкість просунутих можливостей типів.

Перелічення (Enums) в TypeScript

pic

Що таке Перелічення?

Перелічення — це спосіб визначення набору іменованих констант. Вони часто використовуються, коли потрібно мати фіксований набір значень для змінних, таких як дні тижня або попередньо визначені статуси.

Оголошення Перелічень

Перелічення можна створити, використовуючи ключове слово enum:

enum Status {  
 Active,  
 Inactive,  
 Pending  
}  

let userStatus: Status = Status.Active;

Числові та Стрічкові Перелічення

Числові Перелічення: За замовчуванням перелічення призначають числа, починаючи з 0.

enum Status {  
 Active,  
 Inactive,  
 Pending  
}  

let status: Status = Status.Active; // 0

Стрічкові Перелічення: Ви можете призначити конкретні строкові значення елементам перелічення.

enum Status {  
 Active = "ACTIVE",  
 Inactive = "INACTIVE",  
 Pending = "PENDING"  
}  

let status: Status = Status.Active; // "ACTIVE"

Генерики в TypeScript

pic

Що таке Генерики?

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

Оголошення Генеричних Функцій

Генерики оголошуються за допомогою синтаксису <>. Ось як визначити просту генеричну функцію:

function identity(value: T): T {  
 return value;  
}  

let number = identity(42); // number має тип number  
let stringValue = identity("hello"); // stringValue має тип string

Генеричні Інтерфейси та Класи

Ви також можете створювати генеричні інтерфейси та класи:

interface Box {  
 value: T;  
}  

const numberBox: Box = { value: 42 };  
const stringBox: Box = { value: "Hello" };

Використання Генериків з Функціями

Генерики корисні для API викликів, допоміжних функцій і не тільки. Наприклад, розглянемо генеричну функцію для виклику API:

interface User {  
 id: number;  
 name: string;  
 email: string;  
}  

function fetchData(url: string): Promise {  
 return fetch(url).then(response => response.json());  
}  

fetchData("/api/user").then(data => {  
 console.log(data.name); // Доступ до даних користувача  
});

Коли використовувати Генерики замість types чи interfaces

Чому не завжди використовувати Генерики?

Генерики потужні, але вони не завжди є правильним інструментом для кожної ситуації.
Ось коли слід використовувати types або interfaces замість генериків:

  • Використовуйте types та interfaces, коли ви знаєте конкретну структуру або форму даних.
  • Використовуйте генерики, коли вам потрібно гнучке, багаторазове рішення, яке працює з різними типами даних без шкоди для типової безпеки.

Коли використовувати генерики:

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

Приклад: API виклик

Генерики дозволяють нам отримувати різні типи даних без написання повторюваного коду:

interface User {  
 id: number;  
 name: string;  
 email: string;  
}  

interface Product {  
 id: number;  
 product_name: string;  
 product_type: string;  
}  

function fetchData(url: string): Promise {  
 return fetch(url).then(response => response.json());  
}  

fetchData("/api/user").then(user => {  
 console.log(user.name); // Доступ до даних користувача  
});  

fetchData("/api/product").then(product => {  
 console.log(product.product_name); // Доступ до даних продукту  
});

Коли використовувати types та interfaces:

  • Використовуйте types та interfaces, коли вам потрібні фіксовані структури (наприклад, об'єкт User, об'єкт Product).
  • Коли ваші типи не є гнучкими і специфічні для однієї ситуації.

Приклад: Об'єкт User

Якщо ви знаєте структуру User, немає потреби в генеріках. Ви можете безпосередньо визначити інтерфейс:

interface User {  
 id: number;  
 name: string;  
}  

const user: User = { id: 1, name: "Ram" };

Висновок

TypeScript надає потужні інструменти для управління типами та структурами даних, що покращує якість і безпеку коду. Розуміючи відмінності між types, interfaces, enums та generics, ви можете вибрати найкращий інструмент для кожного випадку використання. Молодші розробники можуть почати з простих types та interfaces, в той час як середні та старші розробники можуть використовувати генерики для більш гнучкого, багаторазового коду.

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

Перекладено з: Mastering TypeScript: A Deep Dive into Types, Interfaces, Enums, and Generics

Leave a Reply

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