Generice avansate TypeScript explicate cu exemple

Genericurile din TypeScript oferă o modalitate de a crea componente de cod reutilizabile și flexibile lucrând cu o varietate de tipuri de date. Genericele avansate duc acest concept mai departe prin introducerea de caracteristici suplimentare precum constrângeri, valori implicite și mai multe tipuri, care permit dezvoltatorilor să scrie cod mai robust și mai sigur de tip. În acest articol, vor fi folosite exemple pentru a explora aceste concepte avansate în generice.

Constrângeri generice

Constrângerile limitează tipurile pe care un generic le poate accepta. Acest lucru asigură că tipul transmis unei funcții generice sau unei clase îndeplinește anumite criterii. De exemplu, o constrângere poate fi utilizată pentru a se asigura că tipul generic are o proprietate sau o metodă specifică.

function getLength<T extends { length: number }>(arg: T): number {
    return arg.length;
}

const stringLength = getLength("TypeScript");
const arrayLength = getLength([1, 2, 3]);

În acest exemplu, constrângerea <T extends { length: number }> asigură că argumentul transmis la getLength are o proprietate length.

Generice multiple

TypeScript permite utilizarea mai multor tipuri generice în aceeași funcție, clasă sau interfață. Acest lucru este util atunci când lucrați cu perechi de valori sau alte structuri de date care implică mai multe tipuri.

function pair<T, U>(first: T, second: U): [T, U] {
    return [first, second];
}

const stringNumberPair = pair("TypeScript", 2024);

Această funcție, pair, acceptă două tipuri generice diferite, T și U și returnează un tuplu care conține ambele tipuri.

Tipuri generice implicite

Genericurile din TypeScript pot avea și tipuri implicite. Acest lucru este util atunci când doriți ca un generic să aibă un tip de rezervă dacă nu este furnizat niciun tip specific.

function identity<T = string>(value: T): T {
    return value;
}

const defaultString = identity("Hello");  // T is string
const customNumber = identity<number>(100);  // T is number

În acest exemplu, dacă nu se transmite niciun tip la identity, acesta este implicit la string.

Utilizarea generice cu interfețe

Genericurile pot fi utilizate cu interfețe pentru a defini structuri complexe în care tipurile nu sunt fixe. Acest lucru adaugă flexibilitate modului în care sunt gestionate datele.

interface Container<T> {
    value: T;
}

const stringContainer: Container<string> = { value: "Hello" };
const numberContainer: Container<number> = { value: 42 };

Interfața Container este concepută pentru a păstra o valoare de orice tip, permițând diferite tipuri de containere cu tipuri specifice.

Clasele generice

Clasele din TypeScript pot fi, de asemenea, generice. Acest lucru este util în special atunci când se proiectează clase care funcționează cu diferite tipuri de date, cum ar fi clasele de stocare sau de colectare a datelor.

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);

În acest exemplu, clasa DataStore funcționează cu orice tip de date, oferind o modalitate sigură de a stoca și de a prelua elemente.

Concluzie

Genericele avansate în TypeScript sunt un instrument puternic pentru scrierea de coduri flexibile, reutilizabile și sigure. Folosind constrângeri, mai multe tipuri, valori implicite și generice în clase și interfețe, dezvoltatorii pot scrie cod mai complex și mai robust. Înțelegerea și utilizarea acestor concepte avansate permite o mai mare flexibilitate și asigură siguranța tipului în toate aplicațiile.