Introduzione
Quando si parla di best practice e di codice pulito in JavaScript, spesso ci si concentra su nomi chiari, funzioni piccole, modularità e gestione degli errori. Un aspetto altrettanto importante, ma spesso sottovalutato, è la validazione degli input: controllare i dati in ingresso prima che entrino nella logica applicativa.
Questo sotto-argomento è molto pratico perché quasi ogni applicazione JavaScript riceve dati da qualche fonte esterna: form, API, query string, localStorage, eventi, file, database. Se questi dati non sono validati, il codice diventa fragile, difficile da leggere e più esposto a bug. Al contrario, definire contratti chiari sugli input rende il comportamento del programma più prevedibile e il codice più semplice da mantenere.
In questo tutorial vedremo come scrivere codice pulito in JavaScript applicando un approccio concreto: validare gli input all’ingresso di una funzione, separare la validazione dalla logica di business e restituire messaggi utili quando i dati non rispettano le aspettative.
Codice completo
// Esempio: gestione di un carrello con validazione degli input
// Obiettivo: accettare solo dati coerenti e mantenere la logica pulita.
// Funzione di utilità per validare un prodotto
function validateProduct(product) {
const errors = [];
if (typeof product !== "object" || product === null || Array.isArray(product)) {
return ["Il prodotto deve essere un oggetto valido."];
}
if (typeof product.name !== "string" || product.name.trim() === "") {
errors.push("Il campo ´name´ deve essere una stringa non vuota.");
}
if (typeof product.price !== "number" || Number.isNaN(product.price) || product.price < 0) {
errors.push("Il campo ´price´ deve essere un numero maggiore o uguale a 0.");
}
if (!Number.isInteger(product.quantity) || product.quantity <= 0) {
errors.push("Il campo ´quantity´ deve essere un intero maggiore di 0.");
}
return errors;
}
// Funzione che applica uno sconto solo se i dati sono validi
function calculateDiscountedTotal(product, discountPercentage) {
const productErrors = validateProduct(product);
if (productErrors.length > 0) {
throw new Error(`Prodotto non valido: ${productErrors.join(" ")}`);
}
if (typeof discountPercentage !== "number" || discountPercentage < 0 || discountPercentage > 100) {
throw new Error("Lo sconto deve essere un numero tra 0 e 100.");
}
const subtotal = product.price * product.quantity;
const discount = subtotal * (discountPercentage / 100);
const total = subtotal - discount;
return {
productName: product.name,
subtotal,
discount,
total
};
}
// Funzione che formatta il risultato per la UI
function renderReceipt(data) {
return `
Prodotto: ${data.productName}
Subtotale: €${data.subtotal.toFixed(2)}
Sconto: €${data.discount.toFixed(2)}
Totale: €${data.total.toFixed(2)}
`.trim();
}
// Esempio di utilizzo
const incomingProduct = {
name: "Tastiera meccanica",
price: 89.99,
quantity: 2
};
try {
const result = calculateDiscountedTotal(incomingProduct, 15);
console.log(renderReceipt(result));
} catch (error) {
console.error("Errore:", error.message);
} Spiegazione
Il codice sopra mostra un flusso molto comune: ricevere un oggetto, verificarne la forma, eseguire un calcolo e presentare un risultato. La parte più importante è che la validazione avviene prima della logica di business.
1. Separare validazione e logica
La funzione validateProduct ha un solo compito: controllare che l’oggetto rispetti i requisiti minimi. Non calcola prezzi, non formatta output e non decide cosa fare in caso di successo. Questo rende il codice più leggibile e facile da testare.
La funzione calculateDiscountedTotal invece si occupa del calcolo vero e proprio, ma solo dopo aver verificato che i dati siano validi. In questo modo il contratto della funzione è chiaro: se riceve input corretti, restituisce un risultato affidabile; se riceve input errati, lancia un errore esplicito.
2. Contratti chiari sugli input
Un contratto chiaro significa che ogni funzione dichiara implicitamente o esplicitamente cosa si aspetta. Ad esempio:
- product.name deve essere una stringa non vuota
- product.price deve essere un numero valido e non negativo
- product.quantity deve essere un intero positivo
- discountPercentage deve essere compreso tra 0 e 100
Quando queste regole sono esplicite, chi legge il codice capisce subito come usare la funzione senza dover indovinare. Questo riduce i bug e migliora la collaborazione nel team.
3. Messaggi di errore utili
Un errore generico come "Invalid input" è poco utile. Molto meglio indicare quale campo è sbagliato e perché. Nel nostro esempio, l’utente o lo sviluppatore riceve un messaggio preciso:
- il nome è vuoto
- il prezzo non è valido
- la quantità non è un intero positivo
Questo approccio accelera il debugging e rende il comportamento dell’applicazione più trasparente.
4. Evitare assunzioni pericolose
Un errore comune è fidarsi troppo dei dati in ingresso. Ad esempio, scrivere direttamente product.price * product.quantity senza controlli può produrre NaN, risultati negativi o comportamenti inattesi. La validazione previene questi problemi prima che si propaghino nel resto del sistema.
5. Rendere il codice più testabile
Le funzioni con input ben definiti sono molto più facili da testare. Puoi scrivere test mirati per verificare:
- caso valido: il calcolo funziona correttamente
- caso invalido: viene sollevato un errore appropriato
- caso limite: sconto 0%, sconto 100%, quantità minima, prezzo zero
Più il contratto è chiaro, più i test diventano semplici e affidabili.
Best practice
Ecco alcune pratiche concrete per applicare questo approccio in modo efficace nel codice JavaScript quotidiano.
- Valida il prima possibile: controlla i dati appena entrano in una funzione o in un modulo, non dopo aver iniziato a usarli.
- Separa i controlli dalla logica: una funzione dedicata alla validazione è più pulita di tanti if sparsi nel codice.
- Usa messaggi di errore specifici: indica sempre il campo, il problema e, se utile, il valore atteso.
- Non mischiare validazione e presentazione: la funzione di business non dovrebbe occuparsi di HTML, console o UI.
- Definisci regole coerenti: per esempio, se un prezzo può essere zero, questa regola va documentata e applicata ovunque.
- Evita validazioni duplicate: se un dato è già stato validato in ingresso, non ripetere gli stessi controlli in ogni funzione interna senza motivo.
- Gestisci i casi limite: stringhe vuote, array vuoti, numeri negativi, valori null o undefined.
- Preferisci funzioni piccole: una validazione troppo grande è difficile da leggere; meglio dividerla in controlli più semplici quando il dominio cresce.
Un altro consiglio utile è creare una piccola libreria interna di validazione se il progetto cresce. Ad esempio, puoi avere funzioni come isNonEmptyString, isPositiveNumber o isValidPercentage. Questo migliora la riusabilità e riduce gli errori di implementazione.
Riepilogo
Scrivere codice pulito in JavaScript non significa solo organizzare bene i file o scegliere buoni nomi. Significa anche proteggere la logica applicativa da input errati attraverso validazioni chiare e coerenti.
In questo tutorial abbiamo visto che:
- la validazione degli input è una parte fondamentale del codice pulito
- separare validazione e business logic rende il codice più leggibile
- i contratti chiari aiutano chi usa le funzioni e chi le manutiene
- messaggi di errore precisi migliorano debugging e affidabilità
- un buon approccio alla validazione rende il codice più testabile e robusto
Se vuoi migliorare davvero la qualità del tuo JavaScript, inizia da qui: non fidarti mai dei dati in ingresso, definisci regole esplicite e applicale in modo consistente.
Approfondisci con risorse ufficiali
- MDN Web Docs - JavaScript: documentazione completa e affidabile sul linguaggio
- MDN Web Docs - Error: riferimento utile per la gestione degli errori
- MDN Web Docs - Number: dettagli su numeri, NaN e controlli numerici
- ECMAScript Language Specification: specifica ufficiale del linguaggio JavaScript
- Node.js Documentation: utile se lavori in ambiente server-side
