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
- Anche senza specificare esplicitamente
<number>o<string>, avviene l'inferenza del tipo.identity(42)viene inferito comenumber, eidentity("Hello")viene inferito comestring.
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 cheTdeve essere un tipo con una proprietàlength. Pertanto, i tipi senza una proprietàlengthnon saranno accettati.
Combinazione con keyof
Combinando i generici con keyof, puoi ottenere i nomi delle proprietà in modo type-safe.
1function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
2 return obj[key];
3}
4
5const person = { name: "Bob", age: 30 };
6const personName = getProperty(person, "name"); // string
7console.log(personName);
8
9// const error = getProperty(person, "unknown"); // Error
Kè vincolato dakeyof T, il che indica che possono essere specificate solo le chiavi esistenti inT. Specificare una chiave che non esiste comporterà un errore di compilazione.
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 tipoTusato 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
stringcon<T = string>. Se non viene specificato esplicitamente un tipo,Tassumerà il tipostring.
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 tipoT. 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
mergeaccetta due tipi diversiTeUe 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.