TypeScript 2: digitazioni personalizzate per il modulo npm non tipizzato


93

Dopo aver provato i suggerimenti pubblicati in altri posti , non riesco a far funzionare un progetto dattiloscritto che utilizza un modulo NPM non tipizzato. Di seguito è riportato un esempio minimo e i passaggi che ho provato.

Per questo esempio minimo, faremo finta che lodashnon abbia definizioni di tipo esistenti. Come tale, ignoreremo il pacchetto @types/lodashe proveremo ad aggiungere manualmente il suo file di battitura lodash.d.tsal nostro progetto.

Struttura delle cartelle

  • node_modules
    • lodash
  • src
    • foo.ts
  • battiture
    • personalizzato
      • lodash.d.ts
    • globale
    • index.d.ts
  • package.json
  • tsconfig.json
  • typings.json

Successivamente, i file.

File foo.ts

///<reference path="../typings/custom/lodash.d.ts" />
import * as lodash from 'lodash';

console.log('Weeee');

Il file lodash.d.tsviene copiato direttamente dalla @types/lodashconfezione originale .

File index.d.ts

/// <reference path="custom/lodash.d.ts" />
/// <reference path="globals/lodash/index.d.ts" />

File package.json

{
  "name": "ts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "typings": "./typings/index.d.ts",
  "dependencies": {
    "lodash": "^4.16.4"
  },
  "author": "",
  "license": "ISC"
}

File tsconfig.json

{
  "compilerOptions": {
    "target": "ES6",
    "jsx": "react",
    "module": "commonjs",
    "sourceMap": true,
    "noImplicitAny": true,
    "experimentalDecorators": true,
    "typeRoots" : ["./typings"],
    "types": ["lodash"]
  },
  "include": [
    "typings/**/*",
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ]
}

File typings.json

{
    "name": "TestName",
    "version": false,
    "globalDependencies": {
        "lodash": "file:typings/custom/lodash.d.ts"
    }
}

Come puoi vedere, ho provato molti modi diversi per importare i caratteri:

  1. Importandolo direttamente in foo.ts
  2. Da una typingsproprietà inpackage.json
  3. Usando typeRootsin tsconfig.jsoncon un filetypings/index.d.ts
  4. Utilizzando un esplicito typesintsconfig.json
  5. Includendo la typesdirectory intsconfig.json
  6. Creando un typings.jsonfile personalizzato ed eseguendotypings install

Tuttavia, quando eseguo Typescript:

E:\temp\ts>tsc
error TS2688: Cannot find type definition file for 'lodash'.

Che cosa sto facendo di sbagliato?

Risposte:


202

Sfortunatamente queste cose non sono attualmente molto ben documentate, ma anche se sei riuscito a farlo funzionare, esaminiamo la tua configurazione in modo da capire cosa sta facendo ogni parte e come si relaziona a come il dattiloscritto elabora e carica le battiture.

Per prima cosa esaminiamo l'errore che stai ricevendo:

error TS2688: Cannot find type definition file for 'lodash'.

Questo errore in realtà non proviene dalle tue importazioni o riferimenti o dal tuo tentativo di utilizzare lodash ovunque nei tuoi file ts. Piuttosto deriva da un malinteso su come utilizzare le proprietà typeRootse types, quindi entriamo un po 'più in dettaglio su quelle.

Il problema delle proprietà typeRoots:[]e types:[]è che NON sono modi generici per caricare *.d.tsfile di dichiarazione arbitrari ( ).

Queste due proprietà sono direttamente correlate alla nuova funzionalità TS 2.0 che consente la creazione di pacchetti e il caricamento di dichiarazioni di digitazione dai pacchetti NPM .

Questo è molto importante per capire che funzionano solo con cartelle in formato NPM (cioè una cartella contenente un package.json o index.d.ts ).

L'impostazione predefinita per typeRootsè:

{
   "typeRoots" : ["node_modules/@types"]
}

Per impostazione predefinita, questo significa che il dattiloscritto andrà nella node_modules/@typescartella e proverà a caricare ogni sottocartella che trova lì come pacchetto npm .

È importante capire che questo fallirà se una cartella non ha una struttura simile a un pacchetto npm.

Questo è ciò che sta accadendo nel tuo caso e la fonte del tuo errore iniziale.

Hai cambiato typeRoot in:

{
    "typeRoots" : ["./typings"]
}

Ciò significa che il dattiloscritto ora scansionerà la ./typingscartella per le sottocartelle e proverà a caricare ogni sottocartella che trova come un modulo npm.

Quindi fingiamo che tu abbia appena avuto la typeRootsconfigurazione a cui puntare ./typingsma non hai ancora alcuna types:[]configurazione della proprietà. Probabilmente vedrai questi errori:

error TS2688: Cannot find type definition file for 'custom'.
error TS2688: Cannot find type definition file for 'global'.

Questo perché tscsta scansionando la tua ./typingscartella e trovando le sottocartelle custome global. Quindi sta cercando di interpretarli come una digitazione di tipo pacchetto npm, ma non c'è index.d.tso package.jsonin queste cartelle e quindi viene visualizzato l'errore.

Ora parliamo un po 'della types: ['lodash']proprietà che stai impostando. Cosa fa questo? Per impostazione predefinita, il dattiloscritto caricherà tutte le sottocartelle che trova all'interno del tuo file typeRoots. Se specifichi una types:proprietà, caricherà solo quelle specifiche sottocartelle.

Nel tuo caso gli stai dicendo di caricare la ./typings/lodashcartella ma non esiste. Questo è il motivo per cui ottieni:

error TS2688: Cannot find type definition file for 'lodash'

Quindi riassumiamo ciò che abbiamo imparato. Typescript 2.0 introdotto typeRootse typesper caricare file di dichiarazione impacchettati in pacchetti npm . Se si dispone di digitazioni personalizzate o di singoli d.tsfile sciolti che non sono contenuti in una cartella secondo le convenzioni del pacchetto npm, queste due nuove proprietà non sono ciò che si desidera utilizzare. Typescript 2.0 non cambia realmente come questi verrebbero consumati. Devi solo includere questi file nel tuo contesto di compilazione in uno dei tanti modi standard:

  1. Inserendolo direttamente in un .tsfile: ///<reference path="../typings/custom/lodash.d.ts" />

  2. Compreso ./typings/custom/lodash.d.tsnella tua files: []proprietà.

  3. Incluso ./typings/index.d.tsnella tua files: []proprietà (che quindi include ricorsivamente le altre digitazioni.

  4. Aggiungendo ./typings/**al tuoincludes:

Si spera che sulla base di questa discussione sarai in grado di dire perché i cambiamenti che hai tsconfig.jsonfatto per le tue cose hanno funzionato di nuovo.

MODIFICARE:

Una cosa che ho dimenticato di menzionare è che typeRootse le typesproprietà sono davvero utili solo per il caricamento automatico delle dichiarazioni globali.

Ad esempio se tu

npm install @types/jquery

E stai usando il tsconfig predefinito, quindi quel pacchetto di tipi jquery verrà caricato automaticamente e $sarà disponibile in tutti i tuoi script senza dover fare altro ///<reference/>oimport

La typeRoots:[]proprietà ha lo scopo di aggiungere ulteriori posizioni da cui verranno caricati automaticamente i pacchetti di tipo .

Il types:[]caso d'uso principale della proprietà è disabilitare il comportamento di caricamento automatico (impostandolo su un array vuoto) e quindi elencare solo i tipi specifici che si desidera includere a livello globale.

L'altro modo per caricare i pacchetti di tipo dai vari typeRootsè usare la nuova ///<reference types="jquery" />direttiva. Notare il typesinvece di path. Di nuovo, questo è utile solo per i file di dichiarazione globale, tipicamente quelli che non lo fanno import/export.

Ora, ecco una delle cose che causa confusione con typeRoots. Ricorda, ho detto che typeRootsriguarda l'inclusione globale dei moduli. Ma @types/folderè anche coinvolto nella risoluzione del modulo standard (indipendentemente dalle typeRootsimpostazioni).

In particolare, i moduli in modo esplicito che importano bypassa sempre tutti includes, excludes, files, typeRootse typesle opzioni. Quindi quando lo fai:

import {MyType} from 'my-module';

Tutte le proprietà sopra menzionate vengono completamente ignorate. Le proprietà rilevanti durante la risoluzione del modulo sono baseUrl, pathse moduleResolution.

In sostanza, quando si utilizza nodela risoluzione del modulo, si inizierà a cercare un nome di file my-module.ts, my-module.tsx, my-module.d.tsa partire dalla cartella a cui punta la vostra baseUrlconfigurazione.

Se non trova il file, cercherà una cartella denominata my-modulee quindi cercherà una package.jsoncon una typingsproprietà, se non c'è package.jsono nessuna typingsproprietà all'interno che gli dice quale file caricare, cercherà index.ts/tsx/d.tsall'interno di quella cartella.

Se il problema persiste, cercherà le stesse cose nella node_modulescartella a partire da baseUrl/node_modules.

Inoltre, se non li trova, cercherà baseUrl/node_modules/@typestutte le stesse cose.

Se ancora non ha trovato nulla, inizierà ad andare nella directory principale, a cercare node_modulese node_modules/@typeslì. Continuerà a risalire le directory fino a raggiungere la radice del file system (anche ottenendo i moduli del nodo al di fuori del progetto).

Una cosa che voglio sottolineare è che la risoluzione del modulo ignora completamente qualsiasi typeRootsimpostazione. Quindi, se hai configurato typeRoots: ["./my-types"], questo non verrà cercato durante la risoluzione esplicita del modulo. Serve solo come una cartella in cui è possibile inserire i file di definizione globale che si desidera rendere disponibili per l'intera applicazione senza ulteriore necessità di importazione o riferimento.

Infine, puoi sovrascrivere il comportamento del modulo con le mappature del percorso (cioè la pathsproprietà). Quindi, ad esempio, ho detto che qualsiasi custom typeRootsnon viene consultata quando si cerca di risolvere un modulo. Ma se ti è piaciuto puoi far sì che questo comportamento si verifichi in questo modo:

"paths" :{
     "*": ["my-custom-types/*", "*"]
 }

Ciò che fa è per tutte le importazioni che corrispondono al lato sinistro, prova a modificare l'importazione come nel lato destro prima di provare a includerlo (il *lato destro rappresenta la stringa di importazione iniziale. Ad esempio, se importi:

import {MyType} from 'my-types';

Avrebbe prima provato l'importazione come se avessi scritto:

import {MyType} from 'my-custom-types/my-types'

E poi, se non lo trova, riprova senza il prefisso (il secondo elemento nell'array è proprio il *che significa l'importazione iniziale.

Quindi in questo modo puoi aggiungere ulteriori cartelle per cercare file di dichiarazione personalizzata o anche .tsmoduli personalizzati che desideri essere in grado di creare import.

Puoi anche creare mappature personalizzate per moduli specifici:

"paths" :{
   "*": ["my-types", "some/custom/folder/location/my-awesome-types-file"]
 }

Questo ti lascerebbe fare

import {MyType} from 'my-types';

Ma poi leggi quei tipi da some/custom/folder/location/my-awesome-types-file.d.ts


1
Grazie per la tua risposta dettagliata. Si scopre che le soluzioni funzionano isolatamente, ma non si combinano bene. Chiarisce le cose, quindi ti darò la taglia. Se potessi trovare il tempo per rispondere ad alcuni punti in più, potrebbe essere davvero utile per altre persone. (1) C'è un motivo per cui typeRoots accetta solo una struttura di cartelle specifica? Sembra arbitrario. (2) Se cambio typeRoots, il typescript include ancora le cartelle @types in node_modules, contrariamente alle specifiche. (3) Cos'è pathse in che modo è diverso rispetto includeai fini della digitazione?
Jodiug

1
Ho modificato la risposta, fammi sapere se hai altre domande.
dtabuenc

1
Grazie, ho letto il resto e wow. Ti aiuta a trovare tutto questo. Sembra che qui siano al lavoro molti comportamenti inutilmente complessi. Spero che Typescript renderà le loro proprietà di configurazione un po 'più auto-documentate in futuro.
Jodiug

1
Dovrebbe esserci un collegamento a questa risposta da typescriptlang.org/docs/handbook/tsconfig-json.html
hgoebl

2
L'ultimo esempio non dovrebbe essere come "paths" :{ "my-types": ["some/custom/folder/location/my-awesome-types-file"] }?
Koen.

6

Modifica: non aggiornato. Leggi la risposta sopra.

Continuo a non capire, ma ho trovato una soluzione. Usa quanto segue tsconfig.json:

{
  "compilerOptions": {
    "target": "ES6",
    "jsx": "react",
    "module": "commonjs",
    "sourceMap": true,
    "noImplicitAny": true,
    "experimentalDecorators": true,
    "baseUrl": ".",
    "paths": {
      "*": [
        "./typings/*"
      ]
    }
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ]
}

Rimuovi typings.jsone tutto nella cartella typingstranne lodash.d.ts. Rimuovi anche tutti i ///...riferimenti


3

"*": ["./types/*"] Questa riga nei percorsi tsconfig ha risolto tutto dopo 2 ore di lotta.

{
  "compilerOptions": {
    "moduleResolution": "node",
    "strict": true,
    "baseUrl": ".",
    "paths": {
      "*": ["./types/*"]
    },
    "jsx": "react",
    "types": ["node", "jest"]
  },
  "include": [
    "client/**/*",
    "packages/**/*"
  ],
  "exclude": [
    "node_modules/**/*"
  ]
}

types è il nome della cartella, che si trova accanto a node_module, cioè nel livello della cartella client (o cartella src ) types/third-party-lib/index.d.ts
index.d.ts hadeclare module 'third-party-lib';

Nota: la configurazione di cui sopra è una configurazione incompleta, solo per dare un'idea di come appare con tipi, percorsi, include ed escludi in essa.


1

So che questa è una vecchia domanda, ma gli strumenti per i dattiloscritti sono cambiati continuamente. Penso che l'opzione migliore a questo punto sia solo fare affidamento sulle impostazioni del percorso "include" in tsconfig.json.

  "include": [
        "src/**/*"
    ],

Per impostazione predefinita, a meno che non si apportino modifiche particolari, tutti i file * .ts e tutti i file * .d.ts sotto src/verranno inclusi automaticamente. Penso che questo sia il modo più semplice / migliore per includere file di dichiarazione di tipo personalizzato senza personalizzare typeRootse types.

Riferimento:


0

Vorrei condividere alcune delle mie recenti osservazioni per estendere la descrizione dettagliata che troviamo sopra . Innanzitutto è importante notare che VS Code ha spesso opinioni diverse su come le cose devono essere fatte.

Vediamo un esempio a cui ho lavorato di recente:

src / app / components / plot-plotly / plot-plotly.component.ts:

/// <reference types="plotly.js" />
import * as Plotly from 'plotly.js';

VS Code può lamentarsi che: No need to reference "plotly.js", since it is imported. (no-reference import) tslint(1)

Nel caso in cui avviamo il progetto si compila senza errori, ma nel caso in cui rimuoviamo quella riga apparirà il seguente errore durante l'avvio:

ERROR in src/app/components/plot-plotly/plot-plotly.component.ts:19:21 - error TS2503: Cannot find namespace 'plotly'.

Lo stesso messaggio di errore appare nel caso in cui mettiamo la reference typesdirettiva dopo le istruzioni import.

Importante : il /// <reference types="plotly.js" />deve essere sulla parte anteriore del file di tipo script! Vedere la documentazione correlata: collegamento

Le direttive con tripla barra sono valide solo all'inizio del file che le contiene.

Consiglio anche di leggere la documentazione su tsconfig.json e sulla sezione typeRoot: link

Un pacchetto di tipi è una cartella con un file chiamato index.d.ts o una cartella con un pacchetto.json che ha un campo di tipi.

La direttiva di riferimento di cui sopra funziona nel seguente scenario:

types / plotly.js / index.d.ts: ( link )

  declare namespace plotly {
    export interface ...
  }

E

tsconfig.json:

  "compilerOptions": {
    ...
    "typeRoots": [
      "types/",
      "node_modules/@types"
    ],
    ...  

Nota : Nella configurazione precedente i due "plotly.js" indicano due cartelle e file diversi (libreria / definizione). L'importazione si applica alla cartella "node_modules / plotly.js" (aggiunta da npm install plotly.js), mentre il riferimento si applica a types / plotly.js.

Per il mio progetto per risolvere i reclami di VS Code e l'ambiguità dei "due" plotly.js, ho finito con la seguente configurazione:

  • tutto il file è rimasto nella posizione originale
  • tsconfig.json:
  "compilerOptions": {
    ...
    "typeRoots": [
      "./",
      "node_modules/@types"
    ],
    ...  
  • src / app / components / plot-plotly / plot-plotly.component.ts:
  /// <reference types="types/plotly.js" />
  import * as Plotly from 'plotly.js';

  plotDemo() {

  // types using plotly namespace
  const chunk: plotly.traces.Scatter = {
      x: [1, 2, 3, 4, 5],
      y: [1, 3, 2, 3, 1],
      mode: 'lines+markers',
      name: 'linear',
      line: { shape: 'linear' },
      type: 'scatter'
  };

  // module call using Plotly module import
  Plotly.newPlot(...);
  }

DevDeps sul mio sistema:

  • "ts-node": "~ 8.3.0",
  • "tslint": "~ 5.18.0",
  • "typescript": "~ 3.7.5"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.