Funzioni Parametri Tipizzati

I parametri tipizzati in TypeScript rappresentano il meccanismo fondamentale per garantire type safety nelle funzioni, specificando quali tipi di valori possono essere accettati. La tipizzazione può essere esplicita attraverso annotazioni o inferita dal contesto, permettendo al compilatore di verificare correttezza delle chiamate e prevenire errori a compile time.
Fondamenti e Annotazioni Esplicite
I parametri si tipizzano aggiungendo : Type dopo il nome del parametro. TypeScript richiede tipizzazione esplicita per parametri di funzione quando il tipo non può essere inferito dal contesto.
// Tipizzazione esplicita base
function somma(a: number, b: number): number {
return a + b;
}
// Multiple parametri con tipi diversi
function saluta(nome: string, eta: number, attivo: boolean): string {
return `${nome}, ${eta} anni, ${attivo ? "attivo" : "inattivo"}`;
}
// Tipi primitivi
function processa(
testo: string,
numero: number,
flag: boolean,
simbolo: symbol,
grande: bigint
): void {
console.log(testo, numero, flag, simbolo, grande);
}
Tipi Complessi per Parametri
I parametri possono accettare oggetti, array, tuple e altri tipi complessi con annotazioni strutturali.
// Parametro oggetto con struttura inline
function creaUtente(dati: { nome: string; email: string; eta: number }): void {
console.log(dati);
}
// Parametro array
function elaboraNumeri(numeri: number[]): number {
return numeri.reduce((sum, n) => sum + n, 0);
}
// Parametro tuple
function coordinate(punto: [number, number]): string {
return `X: ${punto[0]}, Y: ${punto[1]}`;
}
// Parametro con array di oggetti
function filtraProdotti(
prodotti: Array<{ nome: string; prezzo: number }>,
prezzoMax: number
): Array<{ nome: string; prezzo: number }> {
return prodotti.filter((p) => p.prezzo <= prezzoMax);
}
Interface e Type Alias per Parametri
Usare interface o type alias migliora leggibilità e riutilizzabilità delle tipizzazioni parametri.
// Con interface
interface Utente {
id: string;
nome: string;
email: string;
}
function aggiornaUtente(utente: Utente, modifiche: Partial<Utente>): Utente {
return { ...utente, ...modifiche };
}
// Con type alias
type Configurazione = {
porta: number;
host: string;
debug: boolean;
};
function avviaServer(config: Configurazione): void {
console.log(`Server su ${config.host}:${config.porta}`);
}
// Type per funzioni callback
type Callback = (risultato: string) => void;
function esegui(operazione: string, callback: Callback): void {
const risultato = `Eseguito: ${operazione}`;
callback(risultato);
}
Union Types per Parametri
I parametri possono accettare multiple tipologie di valori usando union types.
// Union type semplice
function formatta(valore: string | number): string {
if (typeof valore === "string") {
return valore.toUpperCase();
}
return valore.toFixed(2);
}
// Union con literal types
function setModalita(modo: "dev" | "test" | "prod"): void {
console.log(`Modalità: ${modo}`);
}
// Union complessa
function processa(input: string | number | { valore: string | number }): void {
if (typeof input === "object") {
console.log(input.valore);
} else {
console.log(input);
}
}
Parametri Opzionali
I parametri opzionali si dichiarano con ? e hanno tipo T | undefined.
// Parametro opzionale
function creaUtente(nome: string, cognome?: string): void {
if (cognome) {
console.log(`${nome} ${cognome}`);
} else {
console.log(nome);
}
}
creaUtente("Mario");
creaUtente("Mario", "Rossi");
// Multipli parametri opzionali
function configura(porta?: number, host?: string, debug?: boolean): void {
const p = porta ?? 3000;
const h = host ?? "localhost";
const d = debug ?? false;
console.log({ porta: p, host: h, debug: d });
}
// Parametri opzionali devono seguire quelli obbligatori
function crea(nome: string, eta?: number, citta?: string): object {
return { nome, eta, citta };
}
Inferenza Contestuale
TypeScript inferisce tipi dei parametri quando la funzione è usata come callback o assegnata a variabile tipizzata.
// Inferenza da context type
const numeri = [1, 2, 3, 4, 5];
// n è inferito come number
numeri.forEach((n) => console.log(n * 2));
// Inferenza con metodi array
const stringhe = numeri.map((n) => n.toString()); // n: number inferito
// Inferenza da type di variabile
type Operazione = (a: number, b: number) => number;
const somma: Operazione = (a, b) => a + b; // a, b inferiti come number
// Inferenza con callback tipizzate
function esegui(callback: (x: number) => string): void {
callback(42);
}
esegui((x) => x.toFixed(2)); // x inferito come number
Parametri con Readonly
Usare readonly per indicare che la funzione non modificherà il parametro, utile per array e oggetti.
// Array readonly
function somma(numeri: readonly number[]): number {
// numeri.push(10); // ERRORE: non modificabile
return numeri.reduce((sum, n) => sum + n, 0);
}
// Oggetto readonly
function visualizza(config: Readonly<{ porta: number; host: string }>): void {
console.log(config.porta, config.host);
// config.porta = 8080; // ERRORE: non modificabile
}
// Tuple readonly
function distanza(punto: readonly [number, number]): number {
const [x, y] = punto;
return Math.sqrt(x * x + y * y);
}
Generics nei Parametri
I parametri generici permettono funzioni che operano su tipi diversi mantenendo type safety.
// Generic semplice
function primo<T>(array: T[]): T | undefined {
return array[0];
}
const num = primo([1, 2, 3]); // number | undefined
const str = primo(["a", "b"]); // string | undefined
// Multipli generics
function coppia<T, U>(primo: T, secondo: U): [T, U] {
return [primo, secondo];
}
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: number non ha length
Parametri Funzione (Callback)
Tipizzare parametri che sono funzioni per garantire che callback abbiano firma corretta.
// Callback tipizzata inline
function mappa(array: number[], trasforma: (n: number) => number): number[] {
return array.map(trasforma);
}
// Type alias per callback
type Predicato<T> = (item: T) => boolean;
function filtra<T>(array: T[], predicato: Predicato<T>): T[] {
return array.filter(predicato);
}
// Callback con multipli parametri
function riduci(
array: number[],
reducer: (acc: number, current: number, index: number) => number,
iniziale: number
): number {
return array.reduce(reducer, iniziale);
}
// Callback asincrona
async function processoAsync(callback: () => Promise<string>): Promise<void> {
const risultato = await callback();
console.log(risultato);
}
Destructuring nei Parametri
I parametri possono usare destructuring con tipizzazione strutturale.
// Destructuring oggetto con tipi
function stampaPersona({
nome,
eta,
citta,
}: {
nome: string;
eta: number;
citta?: string;
}): void {
console.log(nome, eta, citta);
}
// Con interface
interface Punto {
x: number;
y: number;
}
function sposta({ x, y }: Punto, dx: number, dy: number): Punto {
return { x: x + dx, y: y + dy };
}
// Destructuring array/tuple
function sommaCoordinate([x, y]: [number, number]): number {
return x + y;
}
// Destructuring annidato
function elabora({
utente: { nome, email },
timestamp,
}: {
utente: { nome: string; email: string };
timestamp: Date;
}): void {
console.log(nome, email, timestamp);
}
Index Signatures nei Parametri
Parametri con index signatures permettono oggetti con chiavi dinamiche.
// Oggetto con chiavi dinamiche
function stampaConfig(config: { [chiave: string]: string | number }): void {
for (const key in config) {
console.log(`${key}: ${config[key]}`);
}
}
stampaConfig({ porta: 3000, host: "localhost", timeout: 5000 });
// Record type (più type-safe)
function elaboraDati(dati: Record<string, number>): number {
return Object.values(dati).reduce((sum, n) => sum + n, 0);
}
// Con generic
function trasformaOggetto<T>(
obj: Record<string, T>,
fn: (valore: T) => T
): Record<string, T> {
const risultato: Record<string, T> = {};
for (const key in obj) {
risultato[key] = fn(obj[key]);
}
return risultato;
}
This Parameter
TypeScript permette di tipizzare il valore di this per metodi che dipendono da contesto specifico.
// Tipo this esplicito
function saluta(this: { nome: string }): void {
console.log(`Ciao, ${this.nome}`);
}
const persona = { nome: "Mario" };
saluta.call(persona); // OK
// saluta(); // ERRORE: this deve avere proprietà nome
// This con classe
class Contatore {
valore = 0;
incrementa(this: Contatore): void {
this.valore++;
}
}
// Arrow function evita necessità di this parameter
class ContatoreSicuro {
valore = 0;
incrementa = (): void => {
this.valore++; // this sempre ContatoreSicuro
};
}
Parametri con Never Type
Il tipo never per parametri indica funzioni che non dovrebbero essere chiamate in determinati contesti.
// Funzione che non dovrebbe mai essere raggiunta
function erroreInaspettato(msg: never): never {
throw new Error(`Caso inaspettato: ${msg}`);
}
// Uso in switch exhaustive
type Forma = "cerchio" | "quadrato";
function area(forma: Forma): number {
switch (forma) {
case "cerchio":
return Math.PI * 10 * 10;
case "quadrato":
return 10 * 10;
default:
return erroreInaspettato(forma); // forma è never qui
}
}
Assertion Signatures nei Parametri
Parametri con assertion signatures per validazione e type narrowing.
// Assert function
function assertIsString(valore: any): asserts valore is string {
if (typeof valore !== "string") {
throw new Error("Non è una stringa");
}
}
function processa(input: unknown): void {
assertIsString(input);
// Dopo assert, input è string
console.log(input.toUpperCase());
}
// Assert con proprietà
function assertHasId(obj: any): asserts obj is { id: string } {
if (!obj || typeof obj.id !== "string") {
throw new Error("Oggetto senza id valido");
}
}
Variadic Tuple Types
Parametri che accettano tuple di lunghezza variabile con tipi specifici per posizione.
// Tuple variadic con generics
function concat<T extends any[], U extends any[]>(
arr1: T,
arr2: U
): [...T, ...U] {
return [...arr1, ...arr2];
}
const risultato = concat([1, 2], ["a", "b"]);
// [number, number, string, string]
// Pattern con rest elements
function curry<T extends any[], R>(fn: (...args: T) => R): (...args: T) => R {
return (...args: T) => fn(...args);
}
Conclusioni
La tipizzazione dei parametri in TypeScript fornisce fondamento per type safety, documentazione auto-generata, e migliore developer experience attraverso autocomplete. Annotazioni esplicite garantiscono contratti chiari, mentre inferenza contestuale riduce verbosità. Combinando tipi primitivi, complessi, union, generics, e features avanzate come readonly e assertion signatures, TypeScript permette di esprimere requisiti precisi per parametri, catturando errori a compile time e rendendo il codice più manutenibile e comprensibile senza sacrificare flessibilità.