TypeScript - Interfacce Generiche

Ciao futuro supercampione del coding! Oggi ci imbarcheremo in un viaggio emozionante nel mondo di TypeScript e esploreremo una delle sue funzionalità più potenti: le Interfacce Generiche. Non preoccuparti se sei nuovo alla programmazione - sarò il tuo guida amichevole, e prenderemo tutto passo per passo. Alla fine di questa lezione, sarai sorpreso da quanto hai imparato!

TypeScript - Generic Interfaces

Cos'è un'Interfaccia Generica?

Prima di immergerci nelle interfacce generiche, ripassiamo rapidamente cosa sono le interfacce in TypeScript. Un'interfaccia è come un contratto che definisce la struttura di un oggetto. Ci dice quali proprietà e metodi dovrebbe avere un oggetto.

Immagina ora se potessimo rendere queste interfacce più flessibili, capaci di lavorare con diversi tipi di dati. Ecco dove entrano in gioco le interfacce generiche! Ci permettono di creare interfacce che possono adattarsi a vari tipi di dati, rendendo il nostro codice più riutilizzabile e versatile.

Interfaccia Generica di Base

Iniziamo con un esempio semplice:

interface Box<T> {
contents: T;
}

let numberBox: Box<number> = { contents: 42 };
let stringBox: Box<string> = { contents: "Ciao, TypeScript!" };

In questo esempio, Box è un'interfaccia generica. La <T> è come un placeholder per un tipo che spécificheremo più tardi. Possiamo utilizzare questa interfaccia per creare scatole che possono contenere diversi tipi di elementi:

  • numberBox è una Box che contiene un numero.
  • stringBox è una Box che contiene una stringa.

Non è fantastico? È come avere una scatola magica che può adattarsi a contenere qualsiasi cosa ci mettiamo dentro!

Parametri di Tipo Multipli

Le interfacce generiche possono avere più di un parametro di tipo. guardiamo un esempio:

interface Pair<T, U> {
first: T;
second: U;
}

let pair1: Pair<number, string> = { first: 1, second: "uno" };
let pair2: Pair<boolean, Date> = { first: true, second: new Date() };

Qui, Pair è un'interfaccia generica con due parametri di tipo, T e U. Questo ci permette di creare coppie di elementi dove ciascun elemento può essere di un tipo diverso. È come creare una coppia dinamica di qualsiasi tipo di dati vogliamo!

Interfacce Generiche con Metodi

Le interfacce possono anche includere metodi, e questi metodi possono utilizzare i tipi generici. guardiamo un esempio:

interface Reversible<T> {
data: T[];
reverse(): T[];
}

class NumberArray implements Reversible<number> {
constructor(public data: number[]) {}

reverse(): number[] {
return this.data.slice().reverse();
}
}

let numbers = new NumberArray([1, 2, 3, 4, 5]);
console.log(numbers.reverse()); // Output: [5, 4, 3, 2, 1]

In questo esempio, Reversible è un'interfaccia generica che include un metodo reverse(). La classe NumberArray implements questa interfaccia per i numeri. La bellezza di questo approccio è che potremmo facilmente creare classi simili per stringhe, oggetti o qualsiasi altro tipo!

Interfaccia Generica come Tipo di Funzione

Ora esploriamo come possiamo utilizzare le interfacce generiche per descrivere i tipi di funzione. Questo è dove le cose diventano veramente interessanti!

interface Transformer<T, U> {
(input: T): U;
}

let stringToNumber: Transformer<string, number> = (input) => parseInt(input);

console.log(stringToNumber("42")); // Output: 42

In questo esempio, Transformer è un'interfaccia generica che descrive una funzione. Accetta un input di tipo T e restituisce un valore di tipo U. Creiamo poi una funzione stringToNumber che trasforma una stringa in un numero utilizzando questa interfaccia.

Esempio Reale: Processore di Dati

Guardiamo un esempio più complesso che potresti incontrare nella programmazione reale:

interface DataProcessor<T, U> {
processItem(item: T): U;
processArray(items: T[]): U[];
}

class StringToNumberProcessor implements DataProcessor<string, number> {
processItem(item: string): number {
return parseInt(item);
}

processArray(items: string[]): number[] {
return items.map(item => this.processItem(item));
}
}

let processor = new StringToNumberProcessor();
console.log(processor.processItem("42"));          // Output: 42
console.log(processor.processArray(["1", "2", "3"])); // Output: [1, 2, 3]

In questo esempio, definiamo un'interfaccia DataProcessor che può processare elementi singoli o array di elementi. La classe StringToNumberProcessor implements questa interfaccia per convertire stringhe in numeri. Questo pattern è estremamente utile quando hai bisogno di processare dati in vari modi mantenendo la sicurezza dei tipi.

Conclusione

Complimenti! Hai appena fatto un grande passo nel tuo viaggio di TypeScript imparando le interfacce generiche. Questi strumenti potenti ci permettono di scrivere codice flessibile e riutilizzabile che può lavorare con diversi tipi di dati. Ricorda, la pratica rende perfetti, quindi non aver paura di sperimentare questi concetti nei tuoi progetti personali.

Ecco una tabella di riepilogo dei metodi che abbiamo coperto:

Metodo Descrizione
interface Box<T> Crea un'interfaccia generica per una scatola che può contenere qualsiasi tipo
interface Pair<T, U> Crea un'interfaccia generica per una coppia di elementi di tipi diversi
interface Reversible<T> Crea un'interfaccia generica con un metodo per invertire un array
interface Transformer<T, U> Crea un'interfaccia generica per una funzione che trasforma un tipo in un altro
interface DataProcessor<T, U> Crea un'interfaccia generica per processare elementi singoli o array di elementi

Continua a codificare, continua a imparare, e ricorda - nel mondo di TypeScript, i generici sono il tuo superpotere! ?‍♀️?‍♂️

Credits: Image by storyset