Typescript: TS4023: Die exportierte Variable 'X' hat oder verwendet den Namen 'Y' vom externen Modul 'a / file / path', kann jedoch nicht benannt werden

Erstellt am 18. Nov. 2015  ·  24Kommentare  ·  Quelle: microsoft/TypeScript

Ich versuche, eine Reihe externer TypeScript-Module wie folgt zu erstellen:

// ClassA.ts
export default class ClassA {
  public method() { return true; } 
}

// ModuleB.ts
import ClassA from './ClassA';
var moduleB = {
  ClassA: ClassA
};
export default moduleB;

// ModuleA.ts
import moduleB from './ModuleB';
var moduleA = {
  moduleB: moduleB
}
export default moduleA;

Die Idee ist, dass dies kompiliert und als Bibliothek bereitgestellt wird. Es ist geplant, dass der Verbraucher der Bibliothek eine Instanz von ClassA wie folgt instanziiert:

import moduleA from './ModuleA';
var anInstance = moduleA.moduleB.ClassA();

Dies sieht so aus, als würde es bis zum erwarteten JS kompiliert, aber ich erhalte einen Fehler beim Compiler, über den ich nur schwer weitere Informationen finden kann.

Using tsc v1.6.2
.../src/ModuleA.ts(3,5): error TS4023: Exported variable 'moduleA' has or is using name 'ClassA' from external module ".../src/ClassA" but cannot be named.

Kann hier jemand Licht in dieses Thema bringen? Macht das, was ich versuche, Sinn?

Question

Hilfreichster Kommentar

FWIW erhalten Sie normalerweise schnellere Antworten auf Stack Overflow, aber wir stellen hier gut formulierte Fragen.

Das Problem hierbei ist, dass Sie das Flag --declaration verwenden, dem Compiler jedoch keine Möglichkeit geboten haben, seine Arbeit zu erledigen.

Beim Versuch, ModuleA.d.ts auszugeben, muss der Compiler ein Objekttyp-Literal (z. B. { moduleB: { classA: *mumble?* } } ) schreiben, das die Form des Moduls darstellt. Es gibt jedoch keinen Namen im Gültigkeitsbereich, der direkt auf classA verweist. Daher ist der Typ "kann nicht benannt werden" und es liegt ein Fehler vor.

Wenn Sie import von ClassA zu ModuleA.ts hinzufügen, sollte der Fehler behoben sein.

Alle 24 Kommentare

Oh - wenn dies kein geeigneter Ort ist, um diese Frage zu stellen. Bitte lassen Sie es mich wissen und ich würde es gerne auf einem anderen Medium veröffentlichen.

FWIW erhalten Sie normalerweise schnellere Antworten auf Stack Overflow, aber wir stellen hier gut formulierte Fragen.

Das Problem hierbei ist, dass Sie das Flag --declaration verwenden, dem Compiler jedoch keine Möglichkeit geboten haben, seine Arbeit zu erledigen.

Beim Versuch, ModuleA.d.ts auszugeben, muss der Compiler ein Objekttyp-Literal (z. B. { moduleB: { classA: *mumble?* } } ) schreiben, das die Form des Moduls darstellt. Es gibt jedoch keinen Namen im Gültigkeitsbereich, der direkt auf classA verweist. Daher ist der Typ "kann nicht benannt werden" und es liegt ein Fehler vor.

Wenn Sie import von ClassA zu ModuleA.ts hinzufügen, sollte der Fehler behoben sein.

Hallo Ryan, danke für den Ratschlag zum Überlaufen des RE-Stacks und den Ratschlag. Durch Hinzufügen des Imports zu ModuleA die Dinge wie erwartet kompiliert.

Ein kleiner Kontext hier - mein Ziel hier ist es tatsächlich, Deklarationen für meine externen Module zu generieren. (Etwas, von dem ich glaube, dass es noch nicht ganz funktioniert? # 5039?) Dieser Import mag durchaus notwendig sein, aber es fühlt sich ein wenig seltsam an, wenn ModuleA die Symbole importieren muss, die Modul B bereits exportiert. Das Ergebnis ist, dass ich jedes Mal, wenn ich zum Objektexport von ModuleB hinzufüge, den Import zu ModuleA hinzufügen muss - entweder direkt:

import {moduleB} from './moduleB';
import {ClassA} from './ClassA';

Oder über ModuleB (um die Angabe von Pfaden für ClassA an beiden Stellen zu reduzieren):

import {moduleB, ClassA} from './moduleB';

Ich weiß sehr wenig über den TS-Compiler - vielleicht ist das, was ich sage, völlig unrealistisch, aber zunächst dachte ich, der Compiler könnte daraus schließen, dass ModuleB ein Objekt mit N Symbolen exportiert, also ModuleA braucht diese Symbole? Die Importinformationen für ModuleB sind bereits vorhanden - könnte ModuleA verwenden? Oder wäre das unmöglich, weil der Import für ClassA vollständig innerhalb von ModuleB , sodass der Compiler beim Erstellen von Definitionen für ModuleA auf keinen Fall den Typ für ClassA ableiten kann ModuleA ?

Nochmals vielen Dank, dass Sie sich die Zeit genommen haben, um zu antworten. Ich habe mein Problem jetzt so gelöst, dass das oben Gesagte hauptsächlich aus Neugier besteht - es gibt keine Eile zu antworten.

Ich bin mir nicht sicher, was du genau sagst. Wie sollte ModuleA.d.ts in diesem Vorschlag aussehen?

Der Compiler fügt beim Ausgeben von Deklarationen keine Abhängigkeiten (dh Importanweisungen) hinzu, die im Benutzercode nicht vorhanden waren. Der Fehler, den Sie erhalten, bedeutet, dass der Compiler versucht, eine Typanmerkung für eine exportierte Deklaration zu schreiben, dies jedoch nicht konnte. Dies kann einen von zwei Gründen haben: Entweder ist der Name nicht zugänglich, dh er wird nicht in das aktuelle Modul importiert, oder es gibt eine Deklaration, die die ursprüngliche Deklaration überschattet.

In beiden Fällen besteht Ihre Problemumgehung darin, explizite Typanmerkungen hinzuzufügen. Wenn Sie eine Typanmerkung hinzufügen, wird diese wörtlich in der Ausgabe ausgegeben. Die andere Option besteht darin, sicherzustellen, dass auf den Namen zugegriffen werden kann, dh Sie haben einen Import in das Modul und Sie verstehen, was dies für Ihre Benutzer bedeutet, die Ihr Modul importieren.

@mhegazy sagte:

Der Compiler fügt beim Ausgeben von Deklarationen keine Abhängigkeiten (dh Importanweisungen) hinzu, die im Benutzercode nicht vorhanden waren.

Das Problem ist, dass ich die Importanweisungen im Code nicht immer benötige (offensichtlich, da sie ohne --declarations funktionieren) und sie laut sind, was dazu führt, dass sich Dinge wie tslint über das beschweren "unbenutzter Import". Ich kann verstehen, warum Sie nicht möchten, dass der Compiler Abhängigkeiten zu emittiertem Javascript hinzufügt, aber was ist das Problem beim Hinzufügen dieser zu emittierenden Deklarationen?

Sie können gerne ein Problem protokollieren, um diesen Vorschlag zu verfolgen. Der Grund dafür ist, dass Importe eine Abhängigkeitserklärung sind, und der Compiler sollte keine für Sie erstellen, es sei denn, Sie weisen ihn an, dies zu tun.

Dies könnte tiefere Konsequenzen haben.

Betrachten Sie moduleA -> moduleB -> moduleC -> moduleD .

moduleB ist derjenige, der import { ABC } from 'moduleA' tun muss, um dieses Problem zu umgehen.

Wenn moduleC moduleB und seine Signatur exportiert, kann sie erneut nicht kompiliert werden, da moduleB ABC von moduleA , diesmal jedoch moduleB hat es nicht exportiert.

Dies bedeutet entweder:

  1. moduleC muss eine harte Abhängigkeit von moduleA und ABC importieren
  2. moduleB muss nicht nur ABC importieren, sondern auch erneut exportieren und dann moduleC importieren.

Wenn moduleD ähnliche Dinge tut, müssen Sie im Grunde die gesamte Kette von Abhängigkeiten kennen.

Ich konnte dies nicht testen, um zu beweisen, dass dies der Fall ist, da ich typings und ein Problem mich blockiert: https://github.com/typings/typings/issues/625 ~~

BEARBEITEN: Ich kann es reproduzieren und tatsächlich muss mein moduleD auf moduleA verweisen, um importiert zu werden.
In meinem Beispiel:

  • moduleA => redux
  • moduleB => redux-thunk
  • moduleC => Benutzerdefinierter Code über redux-thunk
  • moduleD => eine Bibliothek
  • ABC => Dispatch Schnittstelle in redux

Ich habe das gleiche Problem, das von @unional beschrieben wird

Bitte öffnen Sie dieses Problem erneut. Die von @unional beschriebenen Probleme sind sehr realistisch. Dies macht es sehr schwierig, Zwischen- / Hilfsbibliotheken über Bibliotheken hinzuzufügen, während dieselben Typen wie das ursprüngliche Modul verwendet werden, das wir erneut exportieren.

Geben Sie https://github.com/Microsoft/TypeScript/issues/9944 Tracks ein, um den Import in der Deklarationsemissionsphase hinzuzufügen.

Danken!

Das Problem beim Hinzufügen des Imports besteht darin, dass sich der Compiler mit noUnusedLocals über den nicht verwendeten Typ beschwert. Ich könnte eine explizite Typanmerkung hinzufügen, aber dann bekomme ich keine Schlussfolgerung. Beispiel:

class Whatever {
  fetch(uri: string): Promise<void> { }
  ensureFetched = MemoizedFunction<(uri: string) => Promise<void>> = memoize((uri: string) => this.fetch(uri))
}

Ich möchte die Typanmerkung für ensureFetched weglassen

Ich habe eine Problemumgehung dafür gefunden:
in tsconfig: include: [ ..., "node_modules/@your_scope/your_library" ]
Viel Glück und viel Spaß: Smiley:

@ salim7 Dies verlangsamt Ihre Kompilierungszeiten (da Sie eine Bibliothek neu kompilieren, die bereits kompiliert werden sollte) und zwingt Sie, den kleinsten gemeinsamen Nenner der

@mhegazy das Problem hat sich im nächsten Szenario reproduziert:

import * as Foo from "./Foo";

export class Bar {
    baz = new Foo.Baz(); // Compiler forgot "Foo." prefix in the type, and throws this error, because "Baz" without perfix is not imported.
    getBaz() { // All the same
       return new Foo.Baz();
    }
}

Die Lösung lautet Typ explizit angeben:

import * as Foo from "./Foo";

export class Bar {
    baz: Foo.Baz = new Foo.Baz(); // ok
    getBaz(): Foo.Baz { // ok
       return new Foo.Baz();
    }
}

Ich kann dies nicht mit dem obigen Beispiel reproduzieren. Bitte melden Sie einen neuen Fehler und geben Sie mehr Kontext an, um das Problem reproduzieren zu können.

@PFight Danke! Das war es für mich!

Ich habe gerade dieses Problem bei der Verwendung von:

export { IMyInterface } from './file'
````

The solution was to do this:
```ts
import { IMyInterface } from './file'
export { IMyInterface }

Aber das sollte wirklich nicht nötig sein.

Hat jemand herausgefunden, wie man mit noUnusedLocals umgeht?

@yordis // @ts-ignore

@pelotom ja das ist völlig meine schuld,

Ich habe über eine Sache nachgedacht und etwas anderes geschrieben.

Hat Typescript das Problem zwischen noUnusedLocals und den Deklarationen behoben?

Meine aktuelle Problemumgehung, die sich für mich wie ein totaler Schmerz anfühlt,

import {SomeInterface} from "./SomeFile";

const _dummySomeInterface : undefined|SomeInterface = undefined;
_dummySomeInterface;

//Code that implicitly uses SomeInterface

Vermeidet noUnusedLocals und ermöglicht nach Möglichkeit auch Typinferenz für generische Schnittstellen.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

dlaberge picture dlaberge  ·  3Kommentare

MartynasZilinskas picture MartynasZilinskas  ·  3Kommentare

zhuravlikjb picture zhuravlikjb  ·  3Kommentare

wmaurer picture wmaurer  ·  3Kommentare

Roam-Cooper picture Roam-Cooper  ·  3Kommentare