Salvi un file, Prettier lo riformatta, ed ESLint segnala subito un errore sulla riga che Prettier ha appena toccato. Entrambi gli strumenti sono configurati, entrambi sono attivi, e invece di aiutarti si combattono a vicenda.
I due strumenti fanno lavori diversi. Il problema è che hanno entrambi un’opinione su una cosa sola: come deve apparire il codice sulla pagina.
Cosa controlla davvero ESLint
ESLint analizza il codice in un albero di sintassi astratto e applica regole sulla sua struttura. Il suo contributo più utile sono le regole che individuano bug reali: una variabile mai definita, un return irraggiungibile dopo un altro return, un import rimasto inutilizzato dopo un refactoring, una chiave duplicata in un oggetto che sovrascrive la prima in silenzio. Sono controlli semantici: ESLint legge cosa fa il codice.
ESLint include anche regole stilistiche, come l’obbligo di usare virgolette singole o punti e virgola e il limite alla lunghezza delle righe — anche se dalla versione 8.53 sono deprecate a favore del progetto separato ESLint Stylistic, e non individuano comunque bug. Rilevano discrepanze di stile, ed è lì che nascono i conflitti con Prettier.
Cosa fa davvero Prettier
Prettier non legge il codice per capirne il significato. Lo analizza, scarta la formattazione originale e ristampa l’intero file secondo regole proprie per indentazione, lunghezza delle righe, stile delle virgolette e dove interrompere le righe troppo lunghe. Eseguilo due volte sullo stesso input e ottieni un output identico byte per byte: è proprio questo l’obiettivo. Non c’è quasi nulla da configurare, per scelta progettuale: un .prettierrc, una manciata di opzioni, e ogni file del progetto ha lo stesso aspetto indipendentemente da chi l’ha scritto.
Esegui prettier --check . in CI e ti dice se ogni file corrisponde a quella formattazione canonica. Non sa e non gli interessa se il codice funziona.
Perché ESLint e Prettier entrano in conflitto
La sovrapposizione è ridotta. Le regole stilistiche di ESLint (stile delle virgolette, punti e virgola, indentazione, virgole finali) si sovrappongono a quelle di Prettier, e i due non sono sempre d’accordo. Quando non lo sono, ottieni il ciclo del primo paragrafo: Prettier riformatta una riga, ESLint segnala il risultato, “correggi” a mano, Prettier lo rimette come prima.
Quindi smetti di chiedere a ESLint di prendere decisioni di formattazione. Lascia che faccia solo quello che Prettier non può fare: individuare bug e pattern scorretti. eslint-config-prettier è una configurazione che disattiva ogni regola ESLint che potrebbe entrare in conflitto o duplicare l’output di Prettier. Non aggiunge nulla. Disattiva soltanto.
Un secondo pacchetto, eslint-plugin-prettier, fa il contrario: esegue Prettier come regola ESLint, così ogni differenza di formattazione compare come un errore ESLint a sé stante. La documentazione ufficiale di Prettier definisce questa configurazione “generalmente non raccomandata”. È più lento, perché ESLint richiama Prettier per ogni file. È anche più rumoroso: una singola riformattazione può trasformarsi in decine di errori ESLint invece che in un solo diff. Esegui Prettier da solo, invece.
Come configurare ESLint e Prettier con il flat config
Da ESLint 9 in poi, il file di configurazione è eslint.config.js, un array flat di oggetti di configurazione che sostituisce il vecchio formato .eslintrc. Se parti da zero con un progetto:
| |
Ti fa qualche domanda e scrive un eslint.config.js. Poi aggiungi Prettier e la configurazione che silenzia le regole in conflitto:
| |
eslint-config-prettier fornisce un entry point per il flat config. Importalo e mettilo come ultimo elemento dell’array. Le voci successive sovrascrivono quelle precedenti, quindi tutto ciò che metti dopo può riattivare una regola già disattivata:
| |
Aggiungi un .prettierrc per le opzioni proprie di Prettier (la maggior parte dei team non ne ha quasi bisogno):
| |
E due script in package.json così i due strumenti girano in modo indipendente:
| |
eslint-config-prettier fornisce anche un controllo da riga di comando che elenca ogni regola nella tua configurazione ancora in conflitto con Prettier:
| |
Il codice di uscita 0 significa nessun conflitto. 2 significa che ne ha trovati, e l’output indica ogni regola così puoi rimuoverla.
Usare eslint-config-prettier con typescript-eslint
Per TypeScript, typescript-eslint si inserisce nello stesso array di flat config e aggiunge le proprie regole semantiche sopra quelle di ESLint puro — tseslint.configs.recommended vieta any scritto direttamente nel codice (no-explicit-any). Catturare un any che si è infiltrato da una chiamata a una libreria non tipizzata è un passo separato e più pesante: passa a recommendedTypeChecked, che richiede parserOptions.project puntato al tuo tsconfig.json perché le regole possano chiedere i tipi reali al compilatore TypeScript. La regola sull’ordine non cambia: eslint-config-prettier va sempre per ultimo.
| |
Nessuno dei due strumenti controlla se i tuoi tipi sono progettati bene. Un generic che andrebbe vincolato con extends passa entrambi i controlli; quello è compito del compilatore, argomento trattato in Generics in TypeScript Spiegati con Esempi.
Eseguire ESLint e Prettier nell’editor e in CI
Nell’editor, imposta “format on save” per eseguire Prettier, e lascia che ESLint segnali separatamente, al salvataggio o in un hook di pre-commit. Con questi file di configurazione pronti, le estensioni dell’editor per i due strumenti funzionano in modo indipendente. Non serve collegare uno all’altro.
In CI, esegui entrambi come step separati:
| |
Un prettier --check fallito significa che qualcuno non ha eseguito il formatter. Un eslint fallito significa che il codice ha un problema reale. Così la differenza resta chiara nel log della CI.
Errori comuni nella configurazione di ESLint e Prettier
Il più comune è installare eslint-plugin-prettier perché un vecchio tutorial lo consiglia, e poi chiedersi perché eslint . è lento e la lista dei problemi è tripla rispetto alle aspettative. Rimuovi il plugin, mantieni eslint-config-prettier, esegui Prettier per conto suo.
Il secondo è l’ordine. Se una configurazione condivisa o un override specifico del progetto si trova dopo eslintConfigPrettier nell’array, riattiva in silenzio una regola che era stata disattivata. Il controllo da riga di comando sopra lo intercetta prima che arrivi a una PR.
Il terzo è lo scope. Questa coppia è pensata per JavaScript e TypeScript. Se stai iniziando un nuovo progetto e preferisci un solo strumento più veloce che faccia entrambi i lavori, guarda Biome. È un compromesso diverso, non un sostituto diretto della configurazione qui sopra.
Cosa fare oggi nel tuo progetto
Apri il tuo package.json. Se eslint-plugin-prettier è presente, disinstallalo. Aggiungi eslint-config-prettier come ultima voce in eslint.config.js, esegui npx eslint-config-prettier eslint.config.js e correggi quello che segnala. Dopo, ogni errore ESLint che vedi riguarda qualcosa che ESLint ha trovato nella struttura del codice, non una riga che Prettier avrebbe riformattato comunque. Non cattura ancora tutto: dimenticare await dentro un ciclo o lasciare una promise rejection non gestita è un errore di logica, non un errore di lint, a meno che tu non aggiunga un plugin apposito — vedi Async/Await e Event Loop in JavaScript Spiegati per capire come appaiono questi errori.