Italiano

Una guida completa alla risoluzione dei moduli TypeScript, che copre le strategie di risoluzione dei moduli classic e node, baseUrl, paths e le migliori pratiche per la gestione dei percorsi di importazione.

Risoluzione dei moduli TypeScript: Demistificare le strategie dei percorsi di importazione

Il sistema di risoluzione dei moduli di TypeScript è un aspetto critico della creazione di applicazioni scalabili e manutenibili. Comprendere come TypeScript individua i moduli in base ai percorsi di importazione è essenziale per organizzare la tua codebase ed evitare errori comuni. Questa guida completa approfondirà le complessità della risoluzione dei moduli TypeScript, coprendo le strategie di risoluzione dei moduli classic e node, il ruolo di baseUrl e paths in tsconfig.json e le migliori pratiche per la gestione efficace dei percorsi di importazione.

Che cos'è la risoluzione dei moduli?

La risoluzione dei moduli è il processo mediante il quale il compilatore TypeScript determina la posizione di un modulo in base all'istruzione di importazione nel tuo codice. Quando scrivi import { SomeComponent } from './components/SomeComponent';, TypeScript deve capire dove risiede effettivamente il modulo SomeComponent sul tuo file system. Questo processo è governato da una serie di regole e configurazioni che definiscono come TypeScript cerca i moduli.

Una risoluzione errata dei moduli può portare a errori di compilazione, errori di runtime e difficoltà nella comprensione della struttura del progetto. Pertanto, una solida comprensione della risoluzione dei moduli è fondamentale per qualsiasi sviluppatore TypeScript.

Strategie di risoluzione dei moduli

TypeScript fornisce due strategie principali di risoluzione dei moduli, configurate tramite l'opzione del compilatore moduleResolution in tsconfig.json:

Risoluzione dei moduli Classic

La strategia di risoluzione dei moduli classic è la più semplice delle due. Cerca i moduli in modo diretto, attraversando l'albero delle directory dal file di importazione.

Come funziona:

  1. Partendo dalla directory contenente il file di importazione.
  2. TypeScript cerca un file con il nome e le estensioni specificati (.ts, .tsx, .d.ts).
  3. Se non viene trovato, si sposta nella directory principale e ripete la ricerca.
  4. Questo processo continua fino a quando il modulo non viene trovato o viene raggiunta la radice del file system.

Esempio:

Considera la seguente struttura del progetto:


project/
├── src/
│   ├── components/
│   │   ├── SomeComponent.ts
│   │   └── index.ts
│   └── app.ts
├── tsconfig.json

Se app.ts contiene l'istruzione di importazione import { SomeComponent } from './components/SomeComponent';, la strategia di risoluzione dei moduli classic eseguirà:

  1. Cerca ./components/SomeComponent.ts, ./components/SomeComponent.tsx o ./components/SomeComponent.d.ts nella directory src.
  2. Se non viene trovato, si sposterà nella directory principale (la radice del progetto) e ripeterà la ricerca, il che è improbabile che abbia successo in questo caso poiché il componente si trova all'interno della cartella src.

Limitazioni:

Quando usare:

La strategia di risoluzione dei moduli classic è generalmente adatta solo per progetti molto piccoli con una struttura di directory semplice e senza dipendenze esterne. I moderni progetti TypeScript dovrebbero quasi sempre utilizzare la strategia di risoluzione dei moduli node.

Risoluzione dei moduli Node

La strategia di risoluzione dei moduli node imita l'algoritmo di risoluzione dei moduli utilizzato da Node.js. Questo la rende la scelta preferita per progetti che prendono di mira Node.js o che utilizzano pacchetti npm, poiché fornisce un comportamento di risoluzione dei moduli coerente e prevedibile.

Come funziona:

La strategia di risoluzione dei moduli node segue un insieme di regole più complesse, dando la priorità alla ricerca all'interno di node_modules e alla gestione di diverse estensioni di file:

  1. Importazioni non relative: se il percorso di importazione non inizia con ./, ../ o /, TypeScript presume che si riferisca a un modulo situato in node_modules. Cercherà il modulo nelle seguenti posizioni:
    • node_modules nella directory corrente.
    • node_modules nella directory principale.
    • ...e così via, fino alla radice del file system.
  2. Importazioni relative: se il percorso di importazione inizia con ./, ../ o /, TypeScript lo tratta come un percorso relativo e cerca il modulo nella posizione specificata, considerando quanto segue:
    • Per prima cosa cerca un file con il nome e le estensioni specificati (.ts, .tsx, .d.ts).
    • Se non viene trovato, cerca una directory con il nome specificato e un file denominato index.ts, index.tsx o index.d.ts all'interno di quella directory (ad esempio, ./components/index.ts se l'importazione è ./components).

Esempio:

Considera la seguente struttura del progetto con una dipendenza dalla libreria lodash:


project/
├── src/
│   ├── utils/
│   │   └── helpers.ts
│   └── app.ts
├── node_modules/
│   └── lodash/
│       └── lodash.js
├── tsconfig.json

Se app.ts contiene l'istruzione di importazione import * as _ from 'lodash';, la strategia di risoluzione dei moduli node eseguirà:

  1. Riconoscere che lodash è un'importazione non relativa.
  2. Cercare lodash nella directory node_modules all'interno della radice del progetto.
  3. Trovare il modulo lodash in node_modules/lodash/lodash.js.

Se helpers.ts contiene l'istruzione di importazione import { SomeHelper } from './SomeHelper';, la strategia di risoluzione dei moduli node eseguirà:

  1. Riconoscere che ./SomeHelper è un'importazione relativa.
  2. Cercare ./SomeHelper.ts, ./SomeHelper.tsx o ./SomeHelper.d.ts nella directory src/utils.
  3. Se nessuno di questi file esiste, cercherà una directory denominata SomeHelper e quindi cercherà index.ts, index.tsx o index.d.ts all'interno di quella directory.

Vantaggi:

Quando usare:

La strategia di risoluzione dei moduli node è la scelta consigliata per la maggior parte dei progetti TypeScript, in particolare quelli che prendono di mira Node.js o che utilizzano pacchetti npm. Fornisce un sistema di risoluzione dei moduli più flessibile e robusto rispetto alla strategia classic.

Configurazione della risoluzione dei moduli in tsconfig.json

Il file tsconfig.json è il file di configurazione centrale per il tuo progetto TypeScript. Ti consente di specificare le opzioni del compilatore, inclusa la strategia di risoluzione dei moduli, e di personalizzare il modo in cui TypeScript gestisce il tuo codice.

Ecco un file tsconfig.json di base con la strategia di risoluzione dei moduli node:


{
  "compilerOptions": {
    "moduleResolution": "node",
    "target": "es5",
    "module": "commonjs",
    "esModuleInterop": true,
    "strict": true,
    "outDir": "dist",
    "sourceMap": true
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}

Opzioni principali del compilerOptions relative alla risoluzione dei moduli:

baseUrl e paths: controllo dei percorsi di importazione

Le opzioni del compilatore baseUrl e paths forniscono potenti meccanismi per controllare il modo in cui TypeScript risolve i percorsi di importazione. Possono migliorare in modo significativo la leggibilità e la manutenibilità del tuo codice consentendoti di utilizzare importazioni assolute e creare mappature di percorsi personalizzate.

baseUrl

L'opzione baseUrl specifica la directory di base per la risoluzione dei nomi dei moduli non relativi. Quando baseUrl è impostato, TypeScript risolverà i percorsi di importazione non relativi relativi alla directory di base specificata anziché alla directory di lavoro corrente.

Esempio:

Considera la seguente struttura del progetto:


project/
├── src/
│   ├── components/
│   │   ├── SomeComponent.ts
│   │   └── index.ts
│   └── app.ts
├── tsconfig.json

Se tsconfig.json contiene quanto segue:


{
  "compilerOptions": {
    "moduleResolution": "node",
    "baseUrl": "./src"
  }
}

Quindi, in app.ts, puoi utilizzare la seguente istruzione di importazione:


import { SomeComponent } from 'components/SomeComponent';

Invece di:


import { SomeComponent } from './components/SomeComponent';

TypeScript risolverà components/SomeComponent relativo alla directory ./src specificata da baseUrl.

Vantaggi dell'utilizzo di baseUrl:

paths

L'opzione paths consente di configurare mappature di percorsi personalizzate per i moduli. Fornisce un modo più flessibile e potente per controllare il modo in cui TypeScript risolve i percorsi di importazione, consentendo di creare alias per i moduli e reindirizzare le importazioni a posizioni diverse.

L'opzione paths è un oggetto in cui ogni chiave rappresenta un modello di percorso e ogni valore è un array di sostituzioni di percorso. TypeScript tenterà di confrontare il percorso di importazione con i modelli di percorso e, se viene trovata una corrispondenza, sostituire il percorso di importazione con i percorsi di sostituzione specificati.

Esempio:

Considera la seguente struttura del progetto:


project/
├── src/
│   ├── components/
│   │   ├── SomeComponent.ts
│   │   └── index.ts
│   └── app.ts
├── libs/
│   └── my-library.ts
├── tsconfig.json

Se tsconfig.json contiene quanto segue:


{
  "compilerOptions": {
    "moduleResolution": "node",
    "baseUrl": "./src",
    "paths": {
      "@components/*": ["components/*"],
      "@mylib": ["../libs/my-library.ts"]
    }
  }
}

Quindi, in app.ts, puoi utilizzare le seguenti istruzioni di importazione:


import { SomeComponent } from '@components/SomeComponent';
import { MyLibraryFunction } from '@mylib';

TypeScript risolverà @components/SomeComponent in components/SomeComponent in base alla mappatura del percorso @components/* e @mylib in ../libs/my-library.ts in base alla mappatura del percorso @mylib.

Vantaggi dell'utilizzo di paths:

Casi d'uso comuni per paths:

Migliori pratiche per la gestione dei percorsi di importazione

La gestione efficace dei percorsi di importazione è fondamentale per la creazione di applicazioni TypeScript scalabili e manutenibili. Ecco alcune best practice da seguire:

Risoluzione dei problemi relativi alla risoluzione dei moduli

I problemi di risoluzione dei moduli possono essere frustranti da eseguire il debug. Ecco alcuni problemi e soluzioni comuni:

Esempi del mondo reale in diversi framework

I principi della risoluzione dei moduli TypeScript si applicano a vari framework JavaScript. Ecco come vengono comunemente utilizzati:

Conclusione

Il sistema di risoluzione dei moduli di TypeScript è uno strumento potente per organizzare la tua codebase e gestire le dipendenze in modo efficace. Comprendendo le diverse strategie di risoluzione dei moduli, il ruolo di baseUrl e paths e le migliori pratiche per la gestione dei percorsi di importazione, puoi creare applicazioni TypeScript scalabili, manutenibili e leggibili. La corretta configurazione della risoluzione dei moduli in tsconfig.json può migliorare significativamente il tuo flusso di lavoro di sviluppo e ridurre il rischio di errori. Sperimenta con diverse configurazioni e trova l'approccio più adatto alle esigenze del tuo progetto.