Was bedeutet "... wird in eine Nicht-Modul-Entität aufgelöst und kann mit diesem Konstrukt nicht importiert werden"?

93

Ich habe einige TypeScript-Dateien:

MyClass.ts

class MyClass {
  constructor() {
  }
}
export = MyClass;

MyFunc.ts

function fn() { return 0; }
export = fn;

MyConsumer.ts

import * as MC from './MyClass';
import * as fn from './MyFunc';
fn();

Dies gibt mir Fehler beim Versuch zu verwenden new

Das Modul "MyClass" wird in eine Nicht-Modul-Entität aufgelöst und kann mit diesem Konstrukt nicht importiert werden.

und beim Versuch anzurufen fn()

Ein Ausdruck, dessen Typ keine Anrufsignatur hat, kann nicht aufgerufen werden.

Was gibt?

Ryan Cavanaugh
quelle
2
Vielen Dank für die Antwort. Ich würde vorschlagen, javascriptals primäres Tag zu entfernen und zu verlassen ecmascript-6, da das primäre Tag hier ist typescript. Die Frage geht fälschlicherweise davon aus, dass export =(eine TS-Funktion) mit gepaart werden kann import ... from, während sie gepaart werden sollteimport = . Es handelt sich im Grunde genommen um den Import / Export von ES6-Modulen im Vergleich zu CJS / AMD.
Estus Flask

Antworten:

156

Warum es nicht funktioniert

import * as MC from './MyClass';

Dies ist eine ES6 / ES2015- importSyntax. Die genaue Bedeutung lautet "Nehmen Sie das geladene Modul- Namespace-Objekt./MyClass und verwenden Sie es lokal als MC". Insbesondere besteht das "Modul- Namespace-Objekt " nur aus einem einfachen Objekt mit Eigenschaften. Ein ES6-Modulobjekt kann nicht als Funktion oder mit aufgerufen werden new.

Um es noch einmal zu sagen: Ein ES6-Modul-Namespace-Objekt kann nicht als Funktion oder mit aufgerufen werden new.

Das , was Sie importmit * as Xvon einem Modul definierten Eigenschaften nur haben. In CommonJS mit niedrigerem Level wird dies möglicherweise nicht vollständig berücksichtigt, aber TypeScript sagt Ihnen, wie sich der Standard verhält.

Was funktioniert?

Sie müssen die CommonJS-artige Importsyntax verwenden, um dieses Modul zu verwenden:

import MC = require('./MyClass');

Wenn Sie beide Module steuern, können Sie export defaultstattdessen Folgendes verwenden:

MyClass.ts

export default class MyClass {
  constructor() {
  }
}

MyConsumer.ts

import MC from './MyClass';

Ich bin traurig darüber; Regeln sind dumm.

Es wäre schön gewesen, die ES6-Importsyntax zu verwenden, aber jetzt muss ich das tun import MC = require('./MyClass');? Es ist so 2013! Lame! Aber Trauer ist ein normaler Bestandteil der Programmierung. Bitte springen Sie im Kübler-Ross-Modell zur fünften Stufe: Akzeptanz.

TypeScript hier sagt Ihnen, dass dies nicht funktioniert, weil es nicht funktioniert. Es gibt Hacks (das Hinzufügen einer namespaceDeklaration zu MyClassist eine beliebte Methode, um so zu tun, als ob dies funktioniert), und sie funktionieren möglicherweise heute in Ihrem speziellen Downleveling-Modulbündler (z. B. Rollup), aber dies ist illusorisch. Es gibt noch keine ES6-Modulimplementierungen in freier Wildbahn, aber das wird nicht für immer so sein.

Stellen Sie sich Ihr zukünftiges Ich vor, versuchen Sie, auf einer nativen ES6-Modulimplementierung zu arbeiten, und stellen Sie fest, dass Sie sich auf einen schwerwiegenden Fehler eingestellt haben, indem Sie versuchen, mithilfe der ES6-Syntax etwas zu tun, was ES6 explizit nicht tut .

Ich möchte meinen nicht standardmäßigen Modullader nutzen

Möglicherweise haben Sie einen Modullader, der "hilfreich" defaultExporte erstellt, wenn keine vorhanden sind. Ich meine, Leute machen Standards aus einem bestimmten Grund, aber Standards zu ignorieren macht manchmal Spaß und wir können denken, dass das eine coole Sache ist.

Ändern Sie MyConsumer.ts in:

import A from './a';

Und geben Sie die allowSyntheticDefaultImportsBefehlszeile oder tsconfig.jsonOption an.

Beachten Sie, dass allowSyntheticDefaultImportsdies das Laufzeitverhalten Ihres Codes überhaupt nicht ändert. Es ist nur ein Flag, das TypeScript mitteilt, dass Ihr Modullader defaultExporte erstellt, wenn keine vorhanden sind. Es wird Ihren Code nicht auf magische Weise in NodeJS funktionieren lassen, wenn dies vorher nicht der Fall war.

Ryan Cavanaugh
quelle
Benötigt der CommonJS-Stil nicht ein CommonJS-Ziel? Gibt es eine Möglichkeit, dies zu erreichen, wenn Sie auf es6 / es2015 abzielen?
Steve Buzonas
Sie können es nicht für ES6 zum
Ryan Cavanaugh
Bin ich richtig im Verständnis, dass es keine Möglichkeit gibt, auf ein CommonJS-Modul zu verweisen, wenn ich auf ES2015-Module abziele export = MyClass? Meine einzige Möglichkeit besteht darin, mein Modul so einzustellen commonjs, dass die Welt schlechter wird, indem ich kein modernes ES verwende.
Micah Zoltu
6
In den Versionshinweisen zu 2.7 steht unter --esModuleInterop"Wir empfehlen dringend, es sowohl auf neue als auch auf bestehende Projekte anzuwenden". Meiner Meinung nach sollte diese Antwort (und der FAQ-Eintrag / die FAQ-Richtlinie in DefinitelyTypeddiesen Links hier) geändert werden, um die neue Haltung widerzuspiegeln.
Alec Mev
1
Also so sehr frech.
Jmealy
25

TypeScript 2.7 bietet Unterstützung durch die Ausgabe neuer Hilfsmethoden: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#support-for-import-d-from-cjs-form- commonjs-modules-with --- esmoduleinterop

Fügen Sie in tsconfig.json diese beiden Einstellungen hinzu:

{
    // Enable support for importing CommonJS modules targeting es6 modules
    "esModuleInterop": true,

    // When using above interop will get missing default export error from type check since
    // modules use "export =" instead of "export default", enable this to ignore errors.
    "allowSyntheticDefaultImports": true
}

Und jetzt können Sie verwenden:

import MyClass from './MyClass';
Michael
quelle
Anstatt zu verwenden, habe esModuleInteropich resolveJsonModuleneben Ihrem anderen Vorschlag verwendet, allowSyntheticDefaultImportsund es hat bei mir funktioniert.
Edgar Quintero
6

Das Hinzufügen meiner 2 Cent hier, falls jemand anderes dieses Problem hat.

Bei meiner Art, das Problem zu umgehen, ohne es zu ändern tsconfig.json(was in einigen Projekten problematisch sein kann), habe ich die Regel für Online einfach deaktiviert.

import MC = require('./MyClass'); // tslint:disable-line

Shahar Hadas
quelle
5

Ich habe diesen Fehler erhalten, als ich versucht habe, ein npm-Debounce-Paket in mein Projekt aufzunehmen.

Als ich die oben akzeptierte Lösung ausprobierte, bekam ich eine Ausnahme:

Die Importzuweisung kann nicht für das Targeting von ECMAScript-Modulen verwendet werden. Verwenden Sie stattdessen 'import * als ns von "mod"', 'import {a} von "mod"', 'import d von "mod"' oder ein anderes Modulformat.

Das hat funktioniert:

import debounce from 'debounce' 
NSjonas
quelle