Wie konfiguriere ich benutzerdefinierte globale Schnittstellen (.d.ts-Dateien) für TypeScript?


76

Ich arbeite derzeit an einem ReactJS-Projekt, das Webpack2 und TypeScript verwendet. Abgesehen von einer Sache funktioniert alles perfekt - ich kann keine Möglichkeit finden, Schnittstellen, die ich selbst geschrieben habe, in separate Dateien zu verschieben, damit sie für die gesamte Anwendung sichtbar sind.

Für Prototyping-Zwecke hatte ich zunächst Schnittstellen in Dateien definiert, die sie verwenden, aber schließlich fügte ich einige hinzu, die in mehreren Klassen benötigt wurden, und dann begannen alle Probleme. tsconfig.jsonUnabhängig davon, welche Änderungen ich an meiner vornehme und wo ich die Dateien ablege, beschweren sich meine IDE und mein Webpack darüber, dass sie keine Namen finden können ( "Name 'IMyInterface' konnte nicht gefunden werden" ).

Hier ist meine aktuelle tsconfig.jsonDatei:

{
  "compilerOptions": {
    "baseUrl": "src",
    "outDir": "build/dist",
    "module": "commonjs",
    "target": "es5",
    "lib": [
      "es6",
      "dom"
    ],
    "typeRoots": [
      "./node_modules/@types",
      "./typings"
    ],
    "sourceMap": true,
    "allowJs": true,
    "jsx": "react",
    "moduleResolution": "node",
    "rootDir": "src",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": false,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true
  },
  "exclude": [
    "node_modules",
    "build",
    "scripts",
    "acceptance-tests",
    "webpack",
    "jest",
    "src/setupTests.ts"
  ],
  "types": [
    "typePatches"
  ]
}

Wie Sie sehen können, befindet sich my tsconfig.jsonim Stammverzeichnis des Projektverzeichnisses, alle Quellen befinden sich in ./src, ich habe meine benutzerdefinierten .d.tsDateien abgelegt ./typingsund in aufgenommen typeRoots.

Ich habe es mit TypeScript 2.1.6 und 2.2.0 getestet und beides funktioniert nicht.

Eine Möglichkeit, alles zum Laufen zu bringen, besteht darin, mein typingsVerzeichnis in srcund dann zu verschieben, import {IMyInterface} from 'typings/blah'aber das fühlt sich für mich nicht richtig an, da ich es nicht verwenden muss. Ich möchte, dass diese Schnittstellen in meiner gesamten Anwendung "magisch" verfügbar sind.

Hier ist eine Beispieldatei app.d.ts:

interface IAppStateProps {}
interface IAppDispatchProps {}
interface IAppProps extends IAppStateProps, IAppDispatchProps {}

Muss ich exportsie oder vielleicht declare? Ich hoffe, ich muss sie nicht in einen Namespace einschließen?!

Update (Oktober 2020)

Angesichts der Tatsache, dass diese Frage immer noch überraschend beliebt ist, wollte ich die Lösung genauer erläutern.

Erstens könnte und sollte es für die Leute verwirrend sein, dass das Schnittstellenbeispiel, das ich am Ende meiner Frage gegeben habe, tatsächlich keine exportSchlüsselwörter enthält, obwohl ich fast sicher bin, dass ich sie zum Zeitpunkt der Anfrage in meinen Dateien hatte Frage. Ich glaube, ich habe sie nicht in die Frage aufgenommen, weil ich dachte, sie hätten keinen Unterschied gemacht, ob sie dort waren oder nicht. Nun, es stellt sich heraus, dass es nicht wahr ist und das exportSchlüsselwort genau das ist, was Sie dazu bringt oder bricht, die Schnittstellen einfach "verwenden" zu können, anstatt sie explizit verwenden zu importmüssen.

So funktioniert es in TypeScript wie folgt:

Wenn Sie eine Schnittstelle / einen Typ wünschen, den Sie einfach verwenden können, ohne sie in das konsumierende Modul importieren zu müssen, muss sich diese Schnittstelle in einer .tsoder idealerweise in einer .d.tsDatei befinden, ohne dass Importe oder Exporte in derselben Datei vorhanden sind . Dies ist von größter Bedeutung, da sobald Sie mindestens eine Sache aus derselben Datei exportieren, diese zu einem Modul wird und alles, was sich in diesem Modul befindet, anschließend von den Verbrauchern importiert werden muss.

Nehmen wir als Beispiel an, Sie möchten einen Typ namens haben Dictionary, den Sie ohne Import verwenden können möchten. Die Erklärung wäre wie folgt:

// types.d.ts
interface Dictionary {}
interface Foo {}
interface Bar {}

Um es zu benutzen, tun Sie einfach:

// consumer.ts
const dict: Dictionary = {};

Es funktioniert jedoch nicht mehr, wenn aus irgendeinem Grund eine der Schnittstellen / Typen in dieser Datei exportiert wird, z.

// types.d.ts
interface Dictionary {}
interface Foo {}
export interface Bar {}

Es funktioniert auch nicht, wenn diese Datei Importe enthält:

// types.d.ts
import { OtherType } from 'other-library';

interface Dictionary {}
interface Foo extends OtherType {}
interface Bar {}

Wenn dies der Fall ist, besteht die einzige Möglichkeit, den DictionaryTyp verwenden zu können, darin, ihn auch zu exportieren und dann in den Verbraucher zu importieren:

// types.d.ts
export interface Dictionary {}
interface Foo {}
export interface Bar {}

// consumer.ts
import { Dictionary } from './types';

const dict: Dictionary = {};
--isolatedModules

Bei der Verwendung des isolatedModulesModul-Flags in TypeScript ist eine zusätzliche Besonderheit zu beachten , die bei Verwendung von Create React App standardmäßig aktiviert ist (und nicht deaktiviert werden kann). .tsDateien MÜSSEN mindestens eine Sache exportieren, da dies sonst der Fall ist die „Alle Dateien müssen Module sein , wenn die‚--isolatedModules‘Flag vorgesehen ist.“ Error. Das bedeutet, dass das Einfügen der DictionarySchnittstelle in types.tsDateien ohne das Schlüsselwort export nicht funktioniert. Es muss entweder ein Export aus einer .tsDatei oder eine Deklaration ohne den Export in eine .d.tsDatei sein:

// types.d.ts
interface Dictionary {}        // works
export interface Dictionary {} // works

// types.ts
interface Dictionary {}        // doesn't work with --isolatedModules enabled
export interface Dictionary {} // works

NB
Wie @dtabuenc in seiner Antwort erwähnt, sind Umgebungsmodule ( .d.tsDateien) nicht korrekt und meine Korrektur sollte nicht als Ratschlag verstanden werden. Es ist nur ein Versuch zu erklären, wie normale Module und Umgebungsmodule in TypeScript funktionieren.

Antworten:


188

Von "magisch verfügbaren Schnittstellen" oder globalen Typen wird dringend abgeraten und sollte größtenteils dem Erbe überlassen werden. Außerdem sollten Sie keine Umgebungsdeklarationsdateien (z. B. d.tsDateien) für Code verwenden, den Sie schreiben. Diese sollen anstelle von externem Nicht-Typoskript-Code stehen (im Wesentlichen werden die Typoskript-Typen in js-Code eingefügt, damit Sie ihn besser in Javascript integrieren können).

Für Code, den Sie schreiben, sollten Sie einfache .tsDateien verwenden, um Ihre Schnittstellen und Typen zu definieren.

Obwohl von globalen Typen abgeraten wird, besteht die Antwort auf Ihr Problem darin, dass .tsTypescript zwei Dateitypen enthält . Diese heißen scriptsund modules.

Alles in einem scriptwird global sein. Wenn Sie Ihre Schnittstellen in einem Skript definieren, ist es global in Ihrer gesamten Anwendung verfügbar (sofern das Skript entweder über ///<reference path="">Tags oder über files:[]oder includes:[]oder die Standardeinstellung **/*.tsin Ihrem Skript in die Kompilierung einbezogen wird tsconfig.json.

Der andere Dateityp ist 'module', und alles in a moduleist für das Modul privat. Wenn Sie etwas aus einem Modul exportieren, steht es anderen Modulen zur Verfügung, wenn diese anderen Module es importieren möchten.

Was macht eine .tsDatei zu einem "Skript" oder einem "Modul"? Nun ... wenn Sie import/exportirgendwo in der Datei verwenden, wird diese Datei zu einem "Modul". Wenn keine import/exportAnweisungen vorhanden sind, handelt es sich um ein globales Skript.

Ich vermute, Sie haben versehentlich importoder exportin Ihren Deklarationen verwendet und daraus ein Modul gemacht, das alle Ihre Schnittstellen innerhalb dieses Moduls in privat verwandelt hat. Wenn Sie möchten, dass sie global sind, stellen Sie sicher, dass Sie keine Import / Export-Anweisungen in Ihrer Datei verwenden.


2
Sie sind absolut richtig! Es stellte sich heraus, dass mein Problem (wie Sie vorgeschlagen haben) darin bestand, dass ich Klassen / Schnittstellen aus anderen Dateien in meine Deklarationsdateien importierte. Sobald ich diese Importe entfernt und nur saubere interface Foo {}Erklärungen hinterlassen habe, hat alles funktioniert. Laut Globals - ich verstehe vollkommen, warum es eine schlechte Praxis ist, Erklärungen magisch verfügbar zu haben, und erwäge, zu einem benutzerdefinierten Modul zu wechseln (tatsächlich versucht declare module MyApp { export interface Foo {} }und könnte es erfolgreich importieren import { {Foo} from 'MyApp'), aber im Moment funktioniert Magie für mich :)
Andris

7
Heiliger Scheißmann. Ich habe nur einen ganzen Tag verschwendet, ohne zu wissen, was ich tat. Danke vielmals. Diese Antwort muss an das offizielle Dokument gesendet werden, wenn sie noch nicht vorhanden ist.
Sam R.

1
Ich habe ein Projekt für eine Woche angehalten und mich gefragt, warum es nicht funktioniert hat. Ich habe privat gelernt, was sonst nicht drei Tage gedauert hätte. Dankeschön dtabuenc
Salathiel Genèse

Vielen herzlichen Dank. Ich habe die SyntheticEventSchnittstelle aus React importiert , um sie in meinen eigenen Typdefinitionen zu verwenden, und alles hat aufgehört zu funktionieren. Ich hätte das früher fangen sollen, aber hier sind wir. Vielen Dank!
Steve Adams

@dtabuenc Können Sie Ihre Aussage unterstützen, dass globale Typen veraltet sind? Wenn ich mir die offizielle Dokumentation ansehe, kann ich diesen Eindruck nicht bekommen.
Tomek
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.