Typescript es6-Importmodul "Datei ist kein Modulfehler"

127

Ich verwende Typoskript 1.6 mit der Syntax der es6-Module.

Meine Dateien sind:

test.ts:

module App {
  export class SomeClass {
    getName(): string {
      return 'name';
    }
  }
}

main.ts:

import App from './test';

var a = new App.SomeClass();

Wenn ich versuche, die main.tsDatei zu kompilieren , wird folgende Fehlermeldung angezeigt:

Fehler TS2306: Die Datei 'test.ts' ist kein Modul.

Wie kann ich das erreichen?

Bazinga
quelle
Ich hatte dieses Problem, ich hatte keinen Konstruktor in der Klasse, fügte einen hinzu und das Problem ging weg
dorriz

Antworten:

139

Erweitert - um basierend auf einigen Kommentaren weitere Details bereitzustellen

Der Fehler

Fehler TS2306: Die Datei 'test.ts' ist kein Modul.

Kommt von der hier beschriebenen Tatsache http://exploringjs.com/es6/ch_modules.html

17. Module

In diesem Kapitel wird erläutert, wie die in ECMAScript 6 integrierten Module funktionieren.

17.1 Übersicht

In ECMAScript 6 werden Module in Dateien gespeichert. Es gibt genau ein Modul pro Datei und eine Datei pro Modul. Sie haben zwei Möglichkeiten, Dinge aus einem Modul zu exportieren. Diese beiden Möglichkeiten können gemischt werden, es ist jedoch normalerweise besser, sie separat zu verwenden.

17.1.1 Exporte mit mehreren Namen

Es kann mehrere benannte Exporte geben:

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}
...

17.1.2 Einzelner Standardexport

Es kann einen einzelnen Standardexport geben. Zum Beispiel eine Funktion:

//------ myFunc.js ------
export default function () { ··· } // no semicolon!

Basierend auf dem oben Gesagten benötigen wir das exportals Teil der Datei test.js. Passen wir den Inhalt folgendermaßen an:

// test.js - exporting es6
export module App {
  export class SomeClass {
    getName(): string {
      return 'name';
    }
  }
  export class OtherClass {
    getName(): string {
      return 'name';
    }
  }
}

Und jetzt können wir es auf drei Arten importieren:

import * as app1 from "./test";
import app2 = require("./test");
import {App} from "./test";

Und wir können importierte Sachen wie diese konsumieren:

var a1: app1.App.SomeClass  = new app1.App.SomeClass();
var a2: app1.App.OtherClass = new app1.App.OtherClass();

var b1: app2.App.SomeClass  = new app2.App.SomeClass();
var b2: app2.App.OtherClass = new app2.App.OtherClass();

var c1: App.SomeClass  = new App.SomeClass();
var c2: App.OtherClass = new App.OtherClass();

und rufen Sie die Methode auf, um sie in Aktion zu sehen:

console.log(a1.getName())
console.log(a2.getName())
console.log(b1.getName())
console.log(b2.getName())
console.log(c1.getName())
console.log(c2.getName())

Der ursprüngliche Teil versucht, die Komplexität bei der Verwendung des Namespace zu verringern

Originalteil:

Ich würde wirklich dringend empfehlen, diese Fragen und Antworten zu überprüfen:

Wie verwende ich Namespaces mit externen TypeScript-Modulen?

Lassen Sie mich den ersten Satz zitieren:

Verwenden Sie keine "Namespaces" in externen Modulen.

Tu das nicht.

Ernsthaft. Halt.

...

In diesem Fall brauchen wir einfach nicht moduledrinnen test.ts. Dies könnte der Inhalt davon angepasst werden test.ts:

export class SomeClass
{
    getName(): string
    {
        return 'name';
    }
}

Lesen Sie hier mehr

Export =

Im vorherigen Beispiel hat jedes Modul nur einen Wert exportiert, als wir jeden Validator verwendet haben. In solchen Fällen ist es umständlich, mit diesen Symbolen über ihren qualifizierten Namen zu arbeiten, wenn eine einzelne Kennung genauso gut wäre.

Die export =Syntax gibt ein einzelnes Objekt an, das aus dem Modul exportiert wird . Dies kann eine Klasse, eine Schnittstelle, ein Modul, eine Funktion oder eine Aufzählung sein. Beim Import wird das exportierte Symbol direkt verwendet und ist durch keinen Namen qualifiziert.

wir können es später so konsumieren:

import App = require('./test');

var sc: App.SomeClass = new App.SomeClass();

sc.getName();

Lesen Sie hier mehr:

Optionales Laden von Modulen und andere erweiterte Ladeszenarien

In einigen Fällen möchten Sie möglicherweise nur unter bestimmten Bedingungen ein Modul laden. In TypeScript können wir das unten gezeigte Muster verwenden, um dieses und andere erweiterte Ladeszenarien zu implementieren und die Modullader direkt aufzurufen, ohne die Typensicherheit zu verlieren.

Der Compiler erkennt, ob jedes Modul im ausgegebenen JavaScript verwendet wird. Für Module, die nur als Teil des Typsystems verwendet werden, werden keine erforderlichen Aufrufe ausgegeben. Dieses Auslesen nicht verwendeter Referenzen ist eine gute Leistungsoptimierung und ermöglicht auch das optionale Laden dieser Module.

Die Kernidee des Musters besteht darin, dass die Anweisung import id = require ('...') uns Zugriff auf die vom externen Modul bereitgestellten Typen gibt. Der Modullader wird dynamisch (durch require) aufgerufen, wie in den if-Blöcken unten gezeigt. Dies nutzt die Referenz-Culling-Optimierung, sodass das Modul nur bei Bedarf geladen wird. Damit dieses Muster funktioniert, ist es wichtig, dass das durch Import definierte Symbol nur an Typpositionen verwendet wird (dh niemals an einer Position, die in JavaScript ausgegeben wird).

Radim Köhler
quelle
1
Aber dies: import App = require ('./ test'); ist nicht die Syntax von es6-Modulen. das ist üblich js. Kann ich das mit der Syntax von es6-Modulen machen?
Bazinga
@JsIsAwesome Sie versuchen, JS-Module mit Typescript-Modulen zu mischen. Sie müssen das eine oder das andere verwenden, keine Mischung aus beiden.
JJJ
Diese Antwort bezieht sich nicht auf die ES6-Syntax
Phiresky
@phiresky, was meinst du?
Radim Köhler
1
Danke, das ist großartig.
Phiresky
24

Die obigen Antworten sind richtig. Aber nur für den Fall ... Ich habe den gleichen Fehler im VS-Code. Musste Datei, die Fehler auslöste, neu speichern / neu kompilieren.

A. Tim
quelle
2
Das hat bei mir funktioniert. Ich habe einfach ein Semikolon entfernt, es erneut hinzugefügt und die Datei erneut gespeichert, und dann hat das Ausführen von Webpack funktioniert. Tolle Zeit, um am Leben zu sein.
Ray Hogan
1
Ich bin an Webstorm gewöhnt und habe nicht bemerkt, dass Dateien nicht automatisch in VS Code gespeichert werden. Diese Antwort hat mir viel Schmerz erspart, danke.
Cib
Es gibt eine Einstellung für das automatische Speichern in VS Code. Ich benutze es nicht, weil VS Code bereits nicht gespeicherte Dateien sichert und ich nicht immer git benutze.
aamarks
13

Wie kann ich das erreichen?

In Ihrem Beispiel wird ein internes TypeScript <1.5- Modul deklariert , das jetzt als Namespace bezeichnet wird . Die alte module App {}Syntax entspricht jetzt namespace App {}. Infolgedessen funktioniert Folgendes:

// test.ts
export namespace App {
    export class SomeClass {
        getName(): string {
            return 'name';
        }
    }
}

// main.ts
import { App } from './test';
var a = new App.SomeClass();

Davon abgesehen ...

Vermeiden Sie den Export von Namespaces und exportieren Sie stattdessen Module (die zuvor als externe Module bezeichnet wurden ). Bei Bedarf können Sie beim Import einen Namespace mit dem folgenden Namespace-Importmuster verwenden :

// test.ts
export class SomeClass {
    getName(): string {
        return 'name';
    }
}

// main.ts
import * as App from './test'; // namespace import pattern
var a = new App.SomeClass();
Shaun Luttin
quelle
1
Es ist immer noch eine gute Praxis? Gemäß dieser Antwort ( stackoverflow.com/a/35706271/2021224 ) ist der Versuch, eine Funktion oder Klasse wie diese zu importieren und sie dann aufzurufen, "gemäß der ES6-Spezifikation illegal".
Andrey Prokhorov
2

Zusätzlich zu A. Tims Antwort gibt es Zeiten, in denen selbst das nicht funktioniert. Sie müssen also:

  1. Schreiben Sie die Importzeichenfolge mit dem Intellisense neu. Manchmal behebt dies das Problem
  2. Starten Sie VS Code neu
ZenVentzi
quelle
1
Gleiches gilt für stackblitz - neu kompilierte Datei, die das Modul importiert und alles funktioniert gut,
Prost
Ich habe dies auch erlebt, als mein Code nicht richtig formatiert war. VSCode hat meinen Klassencode zum Kopieren und Einfügen eingerückt, als ich meine Klassen in ihre eigenen Dateien aufgeteilt habe, und VSCode hat alles nach dem eingerückt, was Angular export class... {nicht gefallen hat, was mir dieses Problem verursacht hat. Nach dem Korrigieren der Formatierung ohne Probleme kompiliert.
Guy Park
0

Zusätzlich zu Tims Antwort trat dieses Problem bei mir auf, als ich eine Umgestaltung einer Datei aufteilte und sie in ihre eigenen Dateien aufteilte.

VSCode hat aus irgendeinem Grund Teile meines [Klassen-] Codes eingerückt, was dieses Problem verursacht hat. Das war anfangs schwer zu bemerken, aber nachdem ich festgestellt hatte, dass der Code eingerückt war, formatierte ich den Code und das Problem verschwand.

Beispielsweise wurde alles nach der ersten Zeile der Klassendefinition beim Einfügen automatisch eingerückt.

export class MyClass extends Something<string> {
    public blah: string = null;

    constructor() { ... }
  }
Guy Park
quelle