Модуль импорта Typescript es6 «Файл не является ошибкой модуля»

127

Я использую машинопись 1.6 с синтаксисом модулей es6.

Мои файлы:

test.ts:

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

main.ts:

import App from './test';

var a = new App.SomeClass();

Когда я пытаюсь скомпилировать main.tsфайл, я получаю такую ​​ошибку:

Ошибка TS2306: Файл test.ts не является модулем.

Как я могу этого добиться?

Bazinga
источник
У меня была эта проблема, у меня не было конструктора в классе, я добавил его, и проблема исчезла
dorriz

Ответы:

139

Расширенный - для предоставления дополнительных сведений на основе некоторых комментариев.

Ошибка

Ошибка TS2306: Файл test.ts не является модулем.

Исходя из факта, описанного здесь http://exploringjs.com/es6/ch_modules.html

17. Модули

В этой главе объясняется, как встроенные модули работают в ECMAScript 6.

17.1 Обзор

В ECMAScript 6 модули хранятся в файлах. На каждый файл приходится ровно один модуль и один файл на модуль. У вас есть два способа экспортировать вещи из модуля. Эти два способа можно смешивать, но обычно лучше использовать их по отдельности.

17.1.1 Экспорт с несколькими именами

Может быть несколько именованных экспортов:

//------ 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 Единый экспорт по умолчанию

Может быть один экспорт по умолчанию. Например, функция:

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

Исходя из вышеизложенного, нам нужен файл exportкак часть файла test.js. Давайте настроим его содержимое следующим образом:

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

И теперь мы можем импортировать его тремя способами:

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

И мы можем потреблять импортированные вещи вот так:

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

и вызовите метод, чтобы увидеть его в действии:

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

Исходная часть пытается помочь уменьшить сложность использования пространства имен

Оригинальная часть:

Я действительно настоятельно рекомендую проверить эти вопросы и ответы:

Как использовать пространства имен с внешними модулями TypeScript?

Приведу первое предложение:

Не используйте «пространства имен» во внешних модулях.

Не делай этого.

Шутки в сторону. Стоп.

...

В этом случае нам просто не нужно moduleвнутри test.ts. Это может быть изменено его содержание test.ts:

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

Подробнее здесь

Экспорт =

В предыдущем примере, когда мы использовали каждый валидатор, каждый модуль экспортировал только одно значение. В таких случаях неудобно работать с этими символами через их квалифицированное имя, когда один идентификатор также подойдет.

export =Синтаксис определяет один объект , который экспортируется из модуля . Это может быть класс, интерфейс, модуль, функция или перечисление. При импорте экспортированный символ потребляется напрямую и не уточняется никаким именем.

позже мы можем использовать это так:

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

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

sc.getName();

Подробнее здесь:

Загрузка дополнительных модулей и другие сценарии расширенной загрузки

В некоторых случаях вам может потребоваться загрузить модуль только при определенных условиях. В TypeScript мы можем использовать показанный ниже шаблон для реализации этого и других расширенных сценариев загрузки, чтобы напрямую вызывать загрузчики модулей без потери безопасности типов.

Компилятор определяет, используется ли каждый модуль в выпущенном JavaScript. Для модулей, которые используются только как часть системы типов, вызовы require не генерируются. Удаление неиспользуемых ссылок является хорошей оптимизацией производительности, а также позволяет дополнительно загружать эти модули.

Основная идея шаблона заключается в том, что оператор import id = require ('...') дает нам доступ к типам, предоставляемым внешним модулем. Загрузчик модуля вызывается (через require) динамически, как показано в блоках if ниже. Это усиливает оптимизацию отбраковки ссылок, так что модуль загружается только при необходимости. Чтобы этот шаблон работал, важно, чтобы символ, определенный с помощью импорта, использовался только в позициях типа (то есть никогда в позиции, которая будет передана в JavaScript).

Радим Кёлер
источник
1
Но это: import App = require ('./ test'); не является синтаксисом модулей es6. это обычный js. Могу ли я сделать это с синтаксисом модулей es6?
Bazinga
@JsIsAwesome Вы пытаетесь смешать модули JS с модулями Typescript. Вам нужно использовать одно или другое, а не их сочетание.
JJJ
Этот ответ не относится к синтаксису
ES6
@phiresky, что ты имеешь в виду?
Radim Köhler
1
Спасибо, отлично.
phiresky
24

Вышеуказанные ответы верны. Но на всякий случай ... Та же ошибка в VS Code. Пришлось повторно сохранить / перекомпилировать файл, который выдавал ошибку.

А. Тим
источник
3
Это сработало для меня. Я просто удалил точку с запятой, снова добавил ее и снова сохранил файл, а затем запустил Webpack. Прекрасное время, чтобы быть живым.
Рэй Хоган,
1
Я привык к Webstorm и не понимал, что файлы не сохраняются автоматически в VS Code. Этот ответ избавил меня от боли, спасибо.
cib
В VS Code есть настройка автоматического сохранения. Я не использую его, потому что VS Code уже выполняет резервное копирование несохраненных файлов, а я не всегда использую git.
aamarks
13

Как я могу этого добиться?

В вашем примере объявляется внутренний модуль TypeScript <1.5 , который теперь называется пространством имен . Старый module App {}синтаксис теперь эквивалентен namespace App {}. В итоге работает следующее:

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

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

Что, как говорится...

Старайтесь избегать экспорта пространств имен и вместо этого экспортируйте модули (которые ранее назывались внешними модулями ). При необходимости вы можете использовать пространство имен при импорте с шаблоном импорта пространства имен следующим образом:

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

// main.ts
import * as App from './test'; // namespace import pattern
var a = new App.SomeClass();
Шон Латтин
источник
1
Это все еще хорошая практика? Согласно этому ответу ( stackoverflow.com/a/35706271/2021224 ), попытка импортировать такую ​​функцию или класс, а затем вызвать ее - «незаконна согласно спецификации ES6».
Андрей Прохоров
2

Помимо ответа А. Тима, бывают случаи, когда даже это не работает, поэтому вам необходимо:

  1. Перепишите строку импорта, используя intellisense. Иногда это решает проблему
  2. Перезапустить VS Code
ZenVentzi
источник
1
то же самое для stackblitz - перекомпилированный файл, который импортирует модуль, и все работает нормально, ура
godblessstrawberry
Я также испытал это, когда мой код был неправильно отформатирован. VSCode сделал отступ в моем коде класса копировать + вставить, когда я разделял свои классы на их собственные файлы, а VSCode сделал отступ все после export class... {, что angular не понравилось, что дало мне эту проблему. После исправления форматирования скомпилировал без проблем.
Guy Park
0

В дополнение к ответу Тима эта проблема возникла у меня, когда я разбивал рефакторинг файла, разбивая его на отдельные файлы.

VSCode по какой-то причине выделил части моего кода [класса] с отступом, что и вызвало эту проблему. Сначала это было трудно заметить, но после того, как я понял, что код имеет отступ, я отформатировал код, и проблема исчезла.

например, все, что находится после первой строки определения класса, было автоматически с отступом во время вставки.

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

    constructor() { ... }
  }
Гай Парк
источник