Moduł importu Typescript es6 „Plik nie jest błędem modułu”

127

Używam maszynopisu 1.6 ze składnią modułów es6.

Moje pliki to:

test.ts:

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

main.ts:

import App from './test';

var a = new App.SomeClass();

Kiedy próbuję skompilować main.tsplik, pojawia się ten błąd:

Błąd TS2306: Plik „test.ts” nie jest modułem.

Jak mogę to osiągnąć?

Bazinga
źródło
Miałem ten problem, nie miałem konstruktora w klasie, dodałem jeden i problem odszedł
dorriz

Odpowiedzi:

139

Rozszerzony - aby podać więcej szczegółów na podstawie niektórych komentarzy

Błąd

Błąd TS2306: Plik „test.ts” nie jest modułem.

Wynika z faktu opisanego tutaj http://exploringjs.com/es6/ch_modules.html

17. Moduły

Ten rozdział wyjaśnia, jak działają wbudowane moduły w ECMAScript 6.

17.1 Przegląd

W ECMAScript 6 moduły są przechowywane w plikach. Na plik przypada dokładnie jeden moduł i jeden plik na moduł. Masz dwa sposoby eksportowania rzeczy z modułu. Te dwa sposoby można mieszać, ale zwykle lepiej jest używać ich osobno.

17.1.1 Wiele nazwanych eksportów

Może istnieć wiele nazwanych eksportów:

//------ 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 Pojedynczy domyślny eksport

Może istnieć jeden domyślny eksport. Na przykład funkcja:

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

W związku z powyższym potrzebujemy export, jako części pliku test.js. Dostosujmy jego zawartość w ten sposób:

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

A teraz możemy go zaimportować na trzy sposoby:

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

I możemy konsumować importowane rzeczy, takie jak ten:

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();

i wywołaj metodę, aby zobaczyć ją w akcji:

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

Oryginalna część stara się pomóc zmniejszyć stopień złożoności wykorzystania przestrzeni nazw

Część oryginalna:

Naprawdę gorąco sugerowałbym sprawdzenie tego pytania i odpowiedzi:

Jak używać przestrzeni nazw z zewnętrznymi modułami TypeScript?

Przytoczę pierwsze zdanie:

Nie używaj „przestrzeni nazw” w modułach zewnętrznych.

Nie rób tego.

Poważnie. Zatrzymać.

...

W tym przypadku po prostu nie potrzebujemy modulewewnątrz test.ts. Może to być dostosowana treść test.ts:

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

Przeczytaj więcej tutaj

Export =

W poprzednim przykładzie, kiedy używaliśmy każdego walidatora, każdy moduł eksportował tylko jedną wartość. W takich przypadkach praca z tymi symbolami poprzez ich kwalifikowaną nazwę jest uciążliwa, gdy wystarczyłby pojedynczy identyfikator.

export =Składnia określa pojedynczy obiekt, który jest eksportowany z modułu . Może to być klasa, interfejs, moduł, funkcja lub wyliczenie. Po zaimportowaniu wyeksportowany symbol jest używany bezpośrednio i nie jest kwalifikowany żadną nazwą.

możemy to później konsumować w ten sposób:

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

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

sc.getName();

Przeczytaj więcej tutaj:

Opcjonalne ładowanie modułów i inne zaawansowane scenariusze ładowania

W niektórych przypadkach możesz chcieć załadować moduł tylko pod pewnymi warunkami. W języku TypeScript możemy użyć wzoru pokazanego poniżej, aby zaimplementować ten i inne zaawansowane scenariusze ładowania, aby bezpośrednio wywołać moduły ładujące bez utraty bezpieczeństwa typów.

Kompilator wykrywa, czy każdy moduł jest używany w emitowanym skrypcie JavaScript. W przypadku modułów, które są używane tylko jako część systemu typów, nie są emitowane żadne żądania. To usuwanie nieużywanych referencji jest dobrą optymalizacją wydajności, a także pozwala na opcjonalne ładowanie tych modułów.

Podstawową ideą wzorca jest to, że instrukcja import id = require ('...') daje nam dostęp do typów ujawnionych przez moduł zewnętrzny. Moduł ładujący jest wywoływany (za pośrednictwem wymagania) dynamicznie, jak pokazano w blokach if poniżej. Wykorzystuje to optymalizację usuwania referencji, dzięki czemu moduł jest ładowany tylko wtedy, gdy jest potrzebny. Aby ten wzorzec działał, ważne jest, aby symbol zdefiniowany przez import był używany tylko w pozycjach typu (tj. Nigdy w pozycji, która zostałaby wyemitowana do JavaScript).

Radim Köhler
źródło
1
Ale to: import App = require ('./ test'); nie jest składnią modułów es6. to jest powszechne js. Czy mogę to zrobić za pomocą składni modułów es6?
Bazinga
@JsIsAwesome Próbujesz mieszać moduły JS z modułami Typescript. Musisz użyć jednego lub drugiego, a nie mieszanki obu.
JJJ
Ta odpowiedź nie odnosi się do składni ES6
phiresky
@phiresky, co masz na myśli?
Radim Köhler
1
Dzięki, to świetnie.
phiresky
24

Powyższe odpowiedzi są prawidłowe. Ale na wszelki wypadek ... Mam ten sam błąd w VS Code. Musiałem ponownie zapisać / ponownie skompilować plik, który generował błąd.

A. Tim
źródło
2
To zadziałało dla mnie. Po prostu usunąłem średnik, ponownie go dodałem i ponownie zapisałem plik, a następnie uruchomiłem Webpack. Świetny czas, żeby żyć.
Ray Hogan
1
Jestem przyzwyczajony do Webstorm i nie zdawałem sobie sprawy, że pliki nie są zapisywane automatycznie w VS Code. Ta odpowiedź zaoszczędziła mi wiele bólu, dzięki.
cib
Istnieje ustawienie automatycznego zapisywania w programie VS Code. Nie używam go, ponieważ VS Code już tworzy kopie zapasowe niezapisanych plików i nie zawsze używam git.
aamarks
13

Jak mogę to osiągnąć?

Twój przykład deklaruje moduł wewnętrzny TypeScript <1.5 , który jest teraz nazywany przestrzenią nazw . Stara module App {}składnia jest teraz odpowiednikiem namespace App {}. W rezultacie działa:

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

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

Biorąc to pod uwagę ...

Staraj się unikać eksportowania przestrzeni nazw i zamiast tego eksportuj moduły (wcześniej nazywane zewnętrznymi ). W razie potrzeby możesz użyć przestrzeni nazw podczas importu z wzorcem importu przestrzeni nazw w następujący sposób:

// 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
źródło
1
Czy to nadal dobra praktyka? Zgodnie z tą odpowiedzią ( stackoverflow.com/a/35706271/2021224 ) próba zaimportowania funkcji lub klasy takiej jak ta, a następnie jej wywołanie - „jest nielegalna zgodnie ze specyfikacją ES6”.
Andrey Prochorow
2

Oprócz odpowiedzi A.Tima są chwile, kiedy nawet to nie działa, więc musisz:

  1. Przepisz importowany ciąg znaków, używając funkcji intellisense. Czasami to rozwiązuje problem
  2. Zrestartuj VS Code
ZenVentzi
źródło
1
to samo dla stackblitz - ponownie skompilowany plik, który importuje moduł i wszystko działa dobrze, wiwaty
godblessstrawberry
Doświadczyłem tego również, gdy mój kod nie został poprawnie sformatowany. VSCode wprowadził wcięcie mojego kodu klasy kopiuj + wklej, gdy dzieliłem moje klasy na własne pliki, a VSCode wciskał wszystko po tym export class... {, co nie podobało się angularowi , dając mi ten problem. Po naprawieniu formatowania skompilowany bez problemu.
Guy Park
0

Oprócz odpowiedzi Tima ten problem wystąpił u mnie, gdy dzieliłem refaktoryzację pliku, dzieląc go na własne pliki.

VSCode z jakiegoś powodu wprowadził wcięcie do części mojego kodu [klasy], co spowodowało ten problem. Na początku było to trudne do zauważenia, ale kiedy zdałem sobie sprawę, że w kodzie są wcięcia, sformatowałem kod i problem zniknął.

na przykład wszystko po pierwszym wierszu definicji klasy było automatycznie wcięte podczas wklejania.

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

    constructor() { ... }
  }
Guy Park
źródło