Funzioni

Le funzioni in TypeScript rappresentano blocchi di codice riutilizzabili che eseguono operazioni specifiche, accettando input attraverso parametri e restituendo output. TypeScript estende le funzioni JavaScript con tipizzazione statica, permettendo di specificare tipi per parametri e valori di ritorno, garantendo type safety e migliorando documentazione e manutenibilità del codice.
Dichiarazione di Funzioni
Le funzioni si possono dichiarare in diversi modi: dichiarazione tradizionale, espressione funzione, o arrow function.
// Dichiarazione tradizionale
function somma(a: number, b: number): number {
return a + b;
}
// Espressione funzione
const sottrai = function (a: number, b: number): number {
return a - b;
};
// Arrow function
const moltiplica = (a: number, b: number): number => {
return a * b;
};
// Arrow function concisa
const dividi = (a: number, b: number): number => a / b;
console.log(somma(5, 3)); // 8
console.log(sottrai(5, 3)); // 2
console.log(moltiplica(5, 3)); // 15
console.log(dividi(6, 3)); // 2
Tipizzazione Parametri e Return
TypeScript richiede tipizzazione esplicita dei parametri, mentre il tipo di ritorno può essere inferito o specificato esplicitamente.
// Tipi espliciti per parametri e return
function saluta(nome: string): string {
return `Ciao, ${nome}!`;
}
// Return type inferito
function doppio(n: number) {
return n * 2; // number inferito
}
// Multipli parametri con tipi diversi
function creaUtente(
nome: string,
eta: number,
attivo: boolean
): { nome: string; eta: number; attivo: boolean } {
return { nome, eta, attivo };
}
// Funzione void (nessun return)
function log(messaggio: string): void {
console.log(messaggio);
}
Parametri Opzionali e Default
I parametri possono essere resi opzionali con ? o avere valori default.
// Parametri opzionali
function salutaPersona(nome: string, cognome?: string): string {
if (cognome) {
return `Ciao, ${nome} ${cognome}`;
}
return `Ciao, ${nome}`;
}
salutaPersona("Mario");
salutaPersona("Mario", "Rossi");
// Parametri con valori default
function configuraServer(
porta: number = 3000,
host: string = "localhost"
): void {
console.log(`Server su ${host}:${porta}`);
}
configuraServer(); // usa default
configuraServer(8080); // custom porta
configuraServer(8080, "0.0.0.0"); // custom entrambi
// Combinazione opzionali e default
function creaConfig(
nome: string,
debug?: boolean,
timeout: number = 5000
): object {
return { nome, debug, timeout };
}
Rest Parameters
I rest parameters raccolgono numero variabile di argomenti in un array.
// Rest parameter
function sommaMultipla(...numeri: number[]): number {
return numeri.reduce((acc, n) => acc + n, 0);
}
console.log(sommaMultipla(1, 2, 3)); // 6
console.log(sommaMultipla(10, 20, 30, 40)); // 100
// Rest parameter dopo parametri normali
function formattaMessaggio(prefisso: string, ...parole: string[]): string {
return `${prefisso}: ${parole.join(" ")}`;
}
console.log(formattaMessaggio("INFO", "Server", "avviato"));
// Rest parameter tipizzato con union
function processa(...items: (string | number)[]): void {
items.forEach((item) => {
if (typeof item === "string") {
console.log(item.toUpperCase());
} else {
console.log(item * 2);
}
});
}
Function Types
Definire tipi per funzioni permette di tipizzare variabili, parametri e return types che sono funzioni.
// Type alias per funzione
type Operazione = (a: number, b: number) => number;
const somma: Operazione = (a, b) => a + b;
const sottrazione: Operazione = (a, b) => a - b;
// Interface per funzione
interface Validatore {
(valore: string): boolean;
}
const nonVuoto: Validatore = (valore) => valore.length > 0;
const isEmail: Validatore = (valore) => valore.includes("@");
// Funzione come parametro
function applica(valore: number, operazione: (n: number) => number): number {
return operazione(valore);
}
console.log(applica(5, (n) => n * 2)); // 10
console.log(applica(5, (n) => n + 10)); // 15
// Funzione che restituisce funzione
function creaMultiplicatore(fattore: number): (n: number) => number {
return (n: number) => n * fattore;
}
const triplo = creaMultiplicatore(3);
console.log(triplo(4)); // 12
Overloading
L’overloading permette di dichiarare multiple firme per una funzione.
// Firme overload
function converti(valore: string): number;
function converti(valore: number): string;
// Implementazione
function converti(valore: string | number): string | number {
if (typeof valore === "string") {
return parseInt(valore, 10);
}
return valore.toString();
}
const num = converti("123"); // number
const str = converti(456); // string
// Overload con diverso numero parametri
function crea(nome: string): { nome: string };
function crea(nome: string, eta: number): { nome: string; eta: number };
function crea(nome: string, eta?: number): any {
if (eta !== undefined) {
return { nome, eta };
}
return { nome };
}
This Parameter
TypeScript permette di tipizzare il valore di this in funzioni.
// Tipizzazione this
interface Utente {
nome: string;
saluta(this: Utente): void;
}
const utente: Utente = {
nome: "Mario",
saluta(this: Utente) {
console.log(`Ciao, sono ${this.nome}`);
},
};
utente.saluta(); // OK
// const fn = utente.saluta;
// fn(); // ERRORE: this mancante
// Arrow function cattura this lessicalmente
class Contatore {
valore = 0;
incrementa = () => {
this.valore++;
};
}
const cont = new Contatore();
const inc = cont.incrementa;
inc(); // OK: this sempre Contatore
Generics nelle Funzioni
I generics permettono funzioni che operano su tipi diversi mantenendo type safety.
// Generic semplice
function primo<T>(array: T[]): T | undefined {
return array[0];
}
const n = primo([1, 2, 3]); // number | undefined
const s = primo(["a", "b"]); // string | undefined
// Multipli generics
function coppia<T, U>(a: T, b: U): [T, U] {
return [a, b];
}
const risultato = coppia("hello", 42); // [string, number]
// Generic con constraints
function lunghezza<T extends { length: number }>(item: T): number {
return item.length;
}
lunghezza("stringa"); // OK
lunghezza([1, 2, 3]); // OK
// lunghezza(123); // ERRORE
// Generic con inferenza
function mappa<T, U>(array: T[], fn: (item: T) => U): U[] {
return array.map(fn);
}
const numeri = mappa([1, 2, 3], (n) => n.toString()); // string[]
Async Functions
Le funzioni async restituiscono Promise e permettono uso di await.
// Funzione async
async function fetchDati(url: string): Promise<any> {
const response = await fetch(url);
return await response.json();
}
// Arrow async
const caricaUtente = async (id: string): Promise<User> => {
const response = await fetch(`/api/utenti/${id}`);
return await response.json();
};
// Async con gestione errori
async function operazioneSicura(): Promise<string | null> {
try {
const risultato = await operazioneRischiosa();
return risultato;
} catch (error) {
console.error(error);
return null;
}
}
// Multiple await
async function processaDati(): Promise<void> {
const dati1 = await fetchDati("/api/1");
const dati2 = await fetchDati("/api/2");
console.log(dati1, dati2);
}
// Await in parallelo
async function paralello(): Promise<void> {
const [dati1, dati2] = await Promise.all([
fetchDati("/api/1"),
fetchDati("/api/2"),
]);
}
IIFE (Immediately Invoked Function Expression)
Funzioni eseguite immediatamente dopo la dichiarazione per creare scope isolato.
// IIFE tradizionale
(function () {
const variabilePrivata = "nascosta";
console.log(variabilePrivata);
})();
// IIFE arrow
(() => {
const config = { porta: 3000 };
console.log(config);
})();
// IIFE con parametri
((nome: string) => {
console.log(`Ciao ${nome}`);
})("TypeScript");
// IIFE async
(async () => {
const dati = await fetchDati("/api/data");
processaDati(dati);
})();
Callback e Higher-Order Functions
Funzioni che accettano altre funzioni come parametri o le restituiscono.
// Callback
function processaArray(
array: number[],
callback: (n: number) => number
): number[] {
return array.map(callback);
}
const raddoppiati = processaArray([1, 2, 3], (n) => n * 2);
// Higher-order function
function creaValidatore(min: number, max: number): (valore: number) => boolean {
return (valore: number) => valore >= min && valore <= max;
}
const validaEta = creaValidatore(18, 100);
console.log(validaEta(25)); // true
// Composizione
function componi<T>(f: (x: T) => T, g: (x: T) => T): (x: T) => T {
return (x: T) => f(g(x));
}
const aggiungi1 = (n: number) => n + 1;
const raddoppia = (n: number) => n * 2;
const trasforma = componi(aggiungi1, raddoppia);
console.log(trasforma(5)); // 11
Closure
Le funzioni possono catturare variabili dall’ambiente circostante.
// Closure base
function creaContatore() {
let conteggio = 0;
return function (): number {
return ++conteggio;
};
}
const contatore = creaContatore();
console.log(contatore()); // 1
console.log(contatore()); // 2
// Closure con stato privato
function creaCalcolatrice() {
let memoria = 0;
return {
aggiungi(n: number) {
memoria += n;
},
sottrai(n: number) {
memoria -= n;
},
getMemoria() {
return memoria;
},
};
}
const calc = creaCalcolatrice();
calc.aggiungi(10);
calc.sottrai(3);
console.log(calc.getMemoria()); // 7
// Factory con closure
function creaFormattatore(prefisso: string) {
return (testo: string): string => {
return `${prefisso}: ${testo}`;
};
}
const errore = creaFormattatore("ERRORE");
const info = creaFormattatore("INFO");
console.log(errore("Qualcosa è andato storto"));
console.log(info("Sistema avviato"));
Metodi di Funzioni
Le funzioni hanno metodi come call, apply, e bind per controllare this e parametri.
// Call
function saluta(this: { nome: string }, prefisso: string) {
return `${prefisso}, ${this.nome}`;
}
const persona = { nome: "Mario" };
console.log(saluta.call(persona, "Ciao")); // "Ciao, Mario"
// Apply
function somma(a: number, b: number, c: number): number {
return a + b + c;
}
const numeri = [1, 2, 3];
console.log(somma.apply(null, numeri)); // 6
// Bind
function moltiplica(this: { fattore: number }, n: number): number {
return n * this.fattore;
}
const obj = { fattore: 10 };
const moltiplicaPer10 = moltiplica.bind(obj);
console.log(moltiplicaPer10(5)); // 50
Ricorsione
Funzioni che chiamano se stesse per risolvere problemi ricorsivi.
// Ricorsione base
function fattoriale(n: number): number {
if (n <= 1) return 1;
return n * fattoriale(n - 1);
}
console.log(fattoriale(5)); // 120
// Ricorsione con accumulatore (tail recursion)
function sommaDa(n: number, acc: number = 0): number {
if (n === 0) return acc;
return sommaDa(n - 1, acc + n);
}
console.log(sommaDa(100)); // 5050
// Ricorsione su strutture
function sommaArray(arr: number[]): number {
if (arr.length === 0) return 0;
return arr[0] + sommaArray(arr.slice(1));
}
console.log(sommaArray([1, 2, 3, 4])); // 10
Conclusioni
Le funzioni in TypeScript rappresentano building blocks fondamentali per organizzare logica riutilizzabile con type safety completa. La tipizzazione di parametri e return values previene errori a compile time, mentre features come generics, overloading, e async/await permettono astrazione potente mantenendo chiarezza. Arrow functions forniscono sintassi concisa con binding lessicale di this, mentre pattern come closure e higher-order functions abilitano programmazione funzionale elegante. Comprendere le diverse forme di dichiarazione e utilizzo delle funzioni risulta essenziale per scrivere codice TypeScript modulare, testabile e manutenibile.