TypeScript Advanced Generics пояснюється на прикладах

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

Загальні обмеження

Обмеження обмежують типи, які може приймати універсал. Це гарантує, що тип, переданий у загальну функцію або клас, відповідає певним критеріям. Наприклад, обмеження можна використовувати, щоб гарантувати, що загальний тип має певну властивість або метод.

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, і повертає кортеж, що містить обидва типи.

Загальні типи за замовчуванням

Генерики у TypeScript також можуть мати типові типи. Це корисно, якщо ви хочете, щоб загальний тип мав запасний тип, якщо не надано конкретного типу.

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 є потужним інструментом для написання гнучкого, багаторазового та безпечного коду. Використовуючи обмеження, кілька типів, значення за замовчуванням і загальні засоби в класах та інтерфейсах, розробники можуть писати більш складний і надійний код. Розуміння та використання цих передових концепцій забезпечує більшу гнучкість і забезпечує безпеку типів у різних програмах.