Generici in TypeScript

Generici in TypeScript

Questo articolo spiega i generici in TypeScript.

YouTube Video

Generici in TypeScript

I generici in TypeScript sono una funzionalità che permette di definire funzioni, classi e interfacce riutilizzabili e sicure dal punto di vista dei tipi, parametrizzando i tipi. L'uso dei generici consente di scrivere codice che non dipende da tipi specifici, permettendo di eseguire le stesse operazioni su diversi tipi.

Nozioni di base sui Generici

I generici funzionano come modelli che accettano tipi come argomenti, permettendo a funzioni e classi di gestire diversi tipi.

Funzioni Generiche

Ecco un esempio di funzione con i tipi degli argomenti specificati usando i generici.

1function identity<T>(value: T): T {
2    return value;
3}
4
5console.log(identity<number>(42));       // 42
6console.log(identity<string>("Hello"));  // Hello
  • T è un argomento di tipo generico che rappresenta i tipi degli argomenti della funzione e il valore restituito. Il tipo effettivo viene determinato quando la funzione viene chiamata.
  • Specificando esplicitamente <number> o <string>, stai specificando il tipo.

I tipi generici funzionano anche senza specificazione esplicita perché TypeScript esegue l'inferenza dei tipi.

1function identity<T>(value: T): T {
2    return value;
3}
4
5console.log(identity(42));       // 42
6console.log(identity("Hello"));  // Hello

Vincoli sui Generici

Imponendo vincoli sui generici, puoi limitarli ad accettare solo tipi specifici.

1function loggingIdentity<T extends { length: number }>(arg: T): T {
2    console.log(arg.length);
3    return arg;
4}
5
6loggingIdentity("Hello");  // 5
7loggingIdentity([1, 2, 3]);  // 3
8
9// loggingIdentity(42);  // Error: number does not have a length property.
  • Specificando T extends { length: number } si indica che T deve essere un tipo con una proprietà length. Pertanto, i tipi senza una proprietà length non saranno accettati.

Classi Generiche

Anche le classi possono essere definite usando i generici. Le classi generiche offrono tipi flessibili per proprietà e metodi.

 1class Box<T> {
 2    private _value: T;
 3
 4    constructor(value: T) {
 5        this._value = value;
 6    }
 7
 8    public getValue(): T {
 9        return this._value;
10    }
11
12    public setValue(value: T): void {
13        this._value = value;
14    }
15}
16
17const numberBox = new Box<number>(100);
18console.log(numberBox.getValue());  // 100
19
20const stringBox = new Box<string>("Hello");
21console.log(stringBox.getValue());  // Hello
  • Box<T> dichiara il tipo T usato all'interno della classe come generico. Ciò consente di riutilizzare la stessa classe per diversi tipi.

Interfacce Generiche

I generici possono essere utilizzati anche con le interfacce.

 1interface Pair<T, U> {
 2    first: T;
 3    second: U;
 4}
 5
 6const numberStringPair: Pair<number, string> = { first: 1, second: "One" };
 7console.log(numberStringPair);  // { first: 1, second: 'One' }
 8
 9const booleanArrayPair: Pair<boolean, number[]> = { first: true, second: [1, 2, 3] };
10console.log(booleanArrayPair);  // { first: true, second: [ 1, 2, 3 ] }
  • Specificando due tipi generici con Pair<T, U>, è possibile definire un oggetto con una combinazione di tipi diversi.

Argomenti di Tipo Predefiniti

È anche possibile specificare un tipo predefinito per gli argomenti di tipo generico.

1function createArray<T = string>(length: number, value: T): T[] {
2    return Array(length).fill(value);
3}
4
5console.log(createArray(3, "a"));   // ['a', 'a', 'a']
6console.log(createArray(3, 100));   // [100, 100, 100]
  • Stiamo impostando l'argomento di tipo predefinito su string con <T = string>. Se non viene specificato esplicitamente un tipo, T assumerà il tipo string.

Alias di Tipo Generico

I generics possono essere utilizzati anche come alias di tipo (type).

 1type Result<T> = {
 2    success: boolean;
 3    data: T;
 4};
 5
 6const successResult: Result<number> = { success: true, data: 42 };
 7const errorResult: Result<string> = { success: false, data: "Error occurred" };
 8
 9console.log(successResult);  // { success: true, data: 42 }
10console.log(errorResult);    // { success: false, data: 'Error occurred' }
  • Result<T> rappresenta un oggetto risultato che contiene dati di tipo T. In questo modo, è possibile creare alias di tipo flessibili utilizzando i generics.

Tipi Generici Multipli

Usando tipi generici multipli, è possibile definire funzioni e classi ancora più versatili.

1function merge<T, U>(obj1: T, obj2: U): T & U {
2    return { ...obj1, ...obj2 };
3}
4
5const person = { name: "Alice" };
6const job = { title: "Engineer" };
7
8const merged = merge(person, job);
9console.log(merged);  // { name: 'Alice', title: 'Engineer' }
  • La funzione merge accetta due tipi diversi T e U e li combina per restituire un nuovo oggetto.

Riepilogo

  • I generics consentono di scrivere codice riutilizzabile e sicuro trattando i tipi come parametri.
  • Usando i generics in funzioni, classi e interfacce, è possibile scrivere logica flessibile che gestisce diversi tipi.
  • Aggiungendo vincoli agli argomenti di tipo o impostando argomenti di tipo predefiniti, è possibile controllare l'ambito dei generics.

Usando i generics, è possibile scrivere codice indipendente dal tipo e generico, sfruttando appieno il potente sistema di tipi di TypeScript.

Puoi seguire l'articolo sopra utilizzando Visual Studio Code sul nostro canale YouTube. Controlla anche il nostro canale YouTube.

YouTube Video