TypeScript 2: Benutzerdefinierte Typisierungen für untypisierte npm-Module


93

Nachdem ich Vorschläge ausprobiert habe, die an anderen Stellen veröffentlicht wurden, kann ich kein Typoskript-Projekt ausführen, das ein untypisiertes NPM-Modul verwendet. Unten ist ein minimales Beispiel und die Schritte, die ich versucht habe.

In diesem minimalen Beispiel geben wir vor, dass lodashkeine Typdefinitionen vorhanden sind. Daher werden wir das Paket ignorieren @types/lodashund versuchen, seine Typisierungsdatei manuell lodash.d.tszu unserem Projekt hinzuzufügen .

Ordnerstruktur

  • Knotenmodule
    • lodash
  • src
    • foo.ts
  • Typisierungen
    • Benutzerdefiniert
      • lodash.d.ts
    • global
    • index.d.ts
  • package.json
  • tsconfig.json
  • typings.json

Als nächstes die Dateien.

Datei foo.ts

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

console.log('Weeee');

Die Datei lodash.d.tswird direkt aus dem Originalpaket kopiert @types/lodash.

Datei index.d.ts

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

Datei 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"
}

Datei 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"
  ]
}

Datei typings.json

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

Wie Sie sehen, habe ich viele verschiedene Möglichkeiten zum Importieren von Typisierungen ausprobiert:

  1. Durch direkten Import in foo.ts
  2. Durch eine typingsImmobilie inpackage.json
  3. Durch die Verwendung typeRootsin tsconfig.jsoneiner Dateitypings/index.d.ts
  4. Durch Verwendung eines expliziten typesintsconfig.json
  5. Durch Einfügen des typesVerzeichnisses intsconfig.json
  6. Durch Erstellen einer benutzerdefinierten typings.jsonDatei und Ausführentypings install

Wenn ich jedoch Typescript ausführe:

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

Was mache ich falsch?

Antworten:


201

Leider sind diese Dinge derzeit nicht sehr gut dokumentiert, aber selbst wenn Sie sie zum Laufen bringen konnten, gehen wir Ihre Konfiguration durch, damit Sie verstehen, was jedes Teil tut und wie es sich darauf bezieht, wie Typoskript Typisierungen verarbeitet und lädt.

Lassen Sie uns zuerst den Fehler durchgehen, den Sie erhalten:

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

Dieser Fehler ist eigentlich nicht auf Ihre Importe oder Referenzen oder Ihren Versuch zurückzuführen, lodash irgendwo in Ihren ts-Dateien zu verwenden. Vielmehr ist es aus einem Missverständnis zu kommen , wie die verwenden typeRootsund typesEigenschaften, so geht sie in ein wenig mehr Details über diese.

Das Besondere an typeRoots:[]und types:[]Eigenschaften ist, dass es sich NICHT um allgemeine Methoden zum Laden beliebiger declaration ( *.d.ts) - Dateien handelt.

Diese beiden Eigenschaften stehen in direktem Zusammenhang mit der neuen TS 2.0-Funktion, mit der Typisierungsdeklarationen aus NPM-Paketen verpackt und geladen werden können .

Dies ist sehr wichtig zu verstehen, dass diese nur mit Ordnern im NPM-Format funktionieren (dh einem Ordner, der eine package.json oder index.d.ts enthält ).

Der Standardwert für typeRootsist:

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

Standardmäßig bedeutet dies, dass Typoskript in den node_modules/@typesOrdner verschoben wird und versucht, jeden dort gefundenen Unterordner als npm-Paket zu laden .

Es ist wichtig zu verstehen, dass dies fehlschlägt, wenn ein Ordner keine npm-paketähnliche Struktur hat.

Dies ist, was in Ihrem Fall passiert, und die Quelle Ihres anfänglichen Fehlers.

Sie haben typeRoot auf Folgendes umgestellt:

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

Dies bedeutet, dass Typoskript jetzt den ./typingsOrdner nach Unterordnern durchsucht und versucht, jeden gefundenen Unterordner als npm-Modul zu laden.

typeRootsStellen wir uns also vor, Sie hätten gerade ein Setup, auf das Sie verweisen ./typingskönnten , aber noch kein types:[]Eigenschafts-Setup. Sie würden wahrscheinlich diese Fehler sehen:

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

Dies liegt daran , tscwird die Scan - ./typingsOrdner und die Suche nach den Unterordner customund global. Es wird dann versucht, diese als Typisierung vom Typ npm zu interpretieren, aber es gibt keine index.d.tsoder package.jsonin diesen Ordnern, und Sie erhalten den Fehler.

Lassen Sie uns nun ein wenig über die types: ['lodash']Eigenschaft sprechen, die Sie festlegen. Was macht das? Standardmäßig lädt Typoskript alle Unterordner, die es in Ihrem findet typeRoots. Wenn Sie eine types:Eigenschaft angeben , werden nur diese bestimmten Unterordner geladen.

In Ihrem Fall weisen Sie es an, den ./typings/lodashOrdner zu laden, aber er existiert nicht. Deshalb erhalten Sie:

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

Fassen wir also zusammen, was wir gelernt haben. Typescript 2.0 wurde eingeführt typeRootsund typeszum Laden von Deklarationsdateien in npm-Paketen . Wenn Sie benutzerdefinierte Typisierungen oder einzelne lose d.tsDateien haben, die nicht in einem Ordner enthalten sind, der den Konventionen des npm-Pakets entspricht, sind diese beiden neuen Eigenschaften nicht das, was Sie verwenden möchten. Typescript 2.0 ändert nicht wirklich, wie diese verbraucht werden. Sie müssen diese Dateien nur auf eine der vielen Standardmethoden in Ihren Kompilierungskontext aufnehmen:

  1. Direkt in eine .tsDatei aufnehmen: ///<reference path="../typings/custom/lodash.d.ts" />

  2. Einschließlich ./typings/custom/lodash.d.tsin Ihrem files: []Eigentum.

  3. Einschließen ./typings/index.d.tsin Ihre files: []Eigenschaft (die dann rekursiv die anderen Typisierungen enthält.

  4. Hinzufügen ./typings/**zu Ihremincludes:

Anhand dieser Diskussion können Sie hoffentlich feststellen, warum die Änderungen, die Sie an Ihren tsconfig.jsonvorgenommenen Dingen vorgenommen haben, wieder funktionieren.

BEARBEITEN:

Eine Sache, die ich vergessen habe zu erwähnen, ist, dass typeRootsund typesEigenschaft wirklich nur zum automatischen Laden globaler Deklarationen nützlich sind .

Zum Beispiel, wenn Sie

npm install @types/jquery

Wenn Sie die Standard-tsconfig verwenden, wird das Paket jquery types automatisch geladen und $ist in allen Ihren Skripten verfügbar, ohne dass Sie weitere Schritte ausführen müssen ///<reference/>oderimport

Die typeRoots:[]Immobilie soll von weiteren Standorten hinzufügen , wo Typ - Pakete werden automatisch geladen frrom werden.

Der types:[]Hauptanwendungsfall der Eigenschaft besteht darin, das automatische Ladeverhalten zu deaktivieren (indem Sie es auf ein leeres Array setzen) und dann nur bestimmte Typen aufzulisten, die Sie global einschließen möchten.

Die andere Möglichkeit, Typpakete aus den verschiedenen zu laden, typeRootsist die Verwendung der neuen ///<reference types="jquery" />Direktive. Beachten Sie die typesstatt path. Dies ist wiederum nur für globale Deklarationsdateien nützlich, normalerweise solche, die dies nicht tun import/export.

Hier ist eines der Dinge, die Verwirrung stiften typeRoots. Denken Sie daran, ich sagte, dass typeRootses um die globale Einbeziehung von Modulen geht. Ist @types/folderaber auch an der Standardmodulauflösung beteiligt (unabhängig von Ihrer typeRootsEinstellung).

Insbesondere explizit den Import Module umgeht immer alle includes, excludes, files, typeRootsund typesOptionen. Also, wenn Sie tun:

import {MyType} from 'my-module';

Alle oben genannten Eigenschaften werden vollständig ignoriert. Die relevanten Eigenschaften während Modul Auflösung sind baseUrl, pathsund moduleResolution.

Grundsätzlich bei der Verwendung von nodeModul - Auflösung, wird es die Suche nach einem Dateinamen beginnen my-module.ts, my-module.tsx, my-module.d.tsin dem Ordner starten , auf dem die baseUrlKonfiguration.

Wenn die Datei nicht gefunden wird, sucht sie nach einem Ordner mit dem Namen my-moduleund sucht dann nach einem package.jsonmit einer typingsEigenschaft. Wenn sich darin eine Eigenschaft befindet package.jsonoder nicht, die typingsangibt, welche Datei geladen werden soll, wird index.ts/tsx/d.tsin diesem Ordner gesucht .

Wenn dies immer noch nicht erfolgreich ist, wird im node_modulesOrdner, der bei Ihnen beginnt, nach denselben Dingen gesucht baseUrl/node_modules.

Wenn diese nicht gefunden werden, wird außerdem nach baseUrl/node_modules/@typesdenselben Dingen gesucht .

Wenn es immer noch nichts findet , wird es beginnen , das übergeordnete Verzeichnis zu gehen und suchen node_modulesund node_modules/@typesdort. Es wird die Verzeichnisse weiter durchsuchen, bis es das Stammverzeichnis Ihres Dateisystems erreicht (sogar Knotenmodule außerhalb Ihres Projekts).

Eine Sache, die ich hervorheben möchte, ist, dass die Modulauflösung alle von typeRootsIhnen festgelegten Einstellungen vollständig ignoriert . Wenn Sie also konfiguriert haben typeRoots: ["./my-types"], wird dies während der expliziten Modulauflösung nicht durchsucht. Es dient nur als Ordner, in dem Sie globale Definitionsdateien ablegen können, die Sie der gesamten Anwendung zur Verfügung stellen möchten, ohne dass Sie sie erneut importieren oder referenzieren müssen.

Zuletzt können Sie das Modulverhalten mit Pfadzuordnungen (dh der pathsEigenschaft) überschreiben . So habe ich zum Beispiel erwähnt, dass typeRootsbeim Versuch, ein Modul aufzulösen , keine benutzerdefinierten Benutzer berücksichtigt werden. Aber wenn Sie möchten, können Sie dieses Verhalten folgendermaßen ausführen:

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

Versuchen Sie für alle Importe, die mit der linken Seite übereinstimmen, den Import wie auf der rechten Seite zu ändern, bevor Sie versuchen, ihn einzuschließen ( *die rechte Seite repräsentiert Ihre anfängliche Importzeichenfolge. Wenn Sie beispielsweise importieren:

import {MyType} from 'my-types';

Es würde zuerst den Import versuchen, als ob Sie geschrieben hätten:

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

Und wenn es dann nicht gefunden wird, versucht es es erneut ohne das Präfix (das zweite Element im Array ist genau *das, was den ersten Import bedeutet.

Auf diese Weise können Sie zusätzliche Ordner hinzufügen, um nach benutzerdefinierten Deklarationsdateien oder sogar nach benutzerdefinierten .tsModulen zu suchen , die Sie verwenden möchten import.

Sie können auch benutzerdefinierte Zuordnungen für bestimmte Module erstellen:

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

Dies würde dich tun lassen

import {MyType} from 'my-types';

Aber dann lesen Sie diese Typen aus some/custom/folder/location/my-awesome-types-file.d.ts


1
Vielen Dank für Ihre ausführliche Antwort. Es stellt sich heraus, dass die Lösungen isoliert funktionieren, sich aber nicht gut mischen lassen. Es klärt die Dinge, also gebe ich dir das Kopfgeld. Wenn Sie die Zeit finden, ein paar weitere Punkte zu beantworten, könnte dies für andere Menschen sehr hilfreich sein. (1) Gibt es einen Grund für typeRoots, nur eine bestimmte Ordnerstruktur zu akzeptieren? Es scheint willkürlich. (2) Wenn ich typeRoots ändere, enthält typescript entgegen der Spezifikation weiterhin die @ types-Ordner in node_modules. (3) Was ist pathsund wie unterscheidet es sich includezum Zwecke der Eingabe?
Jodiug

1
Ich habe die Antwort bearbeitet. Lassen Sie mich wissen, wenn Sie weitere Fragen haben.
dtabuenc

1
Danke, ich habe den Rest gelesen und wow. Wir bitten Sie, all dies zu finden. Es sieht so aus, als ob hier eine Menge unnötig komplexer Verhaltensweisen am Werk sind. Ich hoffe, dass Typescript die Konfigurationseigenschaften in Zukunft etwas selbstdokumentierender macht.
Jodiug

1
Es sollte ein Link zu dieser Antwort seines typescriptlang.org/docs/handbook/tsconfig-json.html
hgoebl

2
Sollte das letzte Beispiel nicht so sein "paths" :{ "my-types": ["some/custom/folder/location/my-awesome-types-file"] }?
Koen.

6

Bearbeiten: veraltet. Lesen Sie die Antwort oben.

Ich verstehe das immer noch nicht, aber ich habe eine Lösung gefunden. Verwenden Sie Folgendes 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"
  ]
}

Entfernen Sie typings.jsonund alles unter Ordner typingsaußer lodash.d.ts. Entfernen Sie auch alle ///...Referenzen


3

"*": ["./types/*"] Diese Zeile in tsconfig-Pfaden reparierte alles nach 2 Stunden Kampf.

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

Typ ist der Ordnername, der neben node_module dh in der Ebene der sitzt Client - Ordner (oder src - Ordner) types/third-party-lib/index.d.ts
index.d.ts hatdeclare module 'third-party-lib';

Hinweis: Die obige Konfiguration ist eine unvollständige Konfiguration, um eine Vorstellung davon zu geben, wie sie mit Typen, Pfaden, Ein- und Ausschlüssen aussieht.


1

Ich weiß, dass dies eine alte Frage ist, aber die Typoskript-Werkzeuge haben sich ständig geändert. Ich denke, die beste Option an dieser Stelle ist, sich nur auf die Pfadeinstellungen "include" in tsconfig.json zu verlassen.

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

Sofern Sie keine besonderen Änderungen vornehmen, werden standardmäßig alle * .ts- und alle * .d.ts-Dateien unter src/automatisch eingeschlossen. Ich denke, dies ist der einfachste / beste Weg, um benutzerdefinierte Typdeklarationsdateien einzuschließen, ohne typeRootsund anzupassen types.

Referenz:


0

Ich möchte einige meiner jüngsten Beobachtung teilen die detaillierte Beschreibung finden wir erweitern oben . Zunächst ist zu beachten, dass VS Code häufig unterschiedliche Meinungen darüber hat, wie Dinge zu tun sind.

Sehen wir uns ein Beispiel an, an dem ich kürzlich gearbeitet habe:

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

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

VS Code kann sich beschweren, dass: No need to reference "plotly.js", since it is imported. (no-reference import) tslint(1)

Wenn wir das Projekt starten, wird es fehlerfrei kompiliert. Wenn wir diese Zeile jedoch entfernen, wird beim Start der folgende Fehler angezeigt:

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

Die gleiche Fehlermeldung wird angezeigt, wenn wir die platzieren reference types Direktive nach den import-Anweisungen platzieren.

Wichtig : Das /// <reference types="plotly.js" />muss sich auf der Vorderseite der Typenskriptdatei befinden! Siehe zugehörige Dokumentation: Link

Triple-Slash-Direktiven sind nur am Anfang ihrer enthaltenen Datei gültig.

Ich empfehle außerdem, die Dokumentation in der Datei tsconfig.json und im Abschnitt typeRoot zu lesen: link

Ein Typpaket ist ein Ordner mit einer Datei namens index.d.ts oder ein Ordner mit einer package.json mit einem Typfeld.

Die obige Referenzrichtlinie funktioniert in folgendem Szenario:

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

  declare namespace plotly {
    export interface ...
  }

UND

tsconfig.json:

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

Hinweis : In der obigen Konfiguration bedeuten die beiden "plotly.js" zwei verschiedene Ordner und Dateien (Bibliothek / Definition). Der Import gilt für den Ordner "node_modules / plotly.js" (hinzugefügt vonnpm install plotly.js ), während der Verweis für types / plotly.js gilt.

Damit mein Projekt die VS-Code-Beschwerden und die Mehrdeutigkeit der "zwei" plotly.js beheben kann, habe ich die folgende Konfiguration erhalten:

  • Alle Dateien blieben am ursprünglichen Speicherort
  • 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 auf meinem System:

  • "ts-node": "~ 8.3.0",
  • "tslint": "~ 5.18.0",
  • "Typoskript": "~ 3.7.5"
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.