Что означает «… преобразуется в немодульный объект и не может быть импортирован с помощью этой конструкции»?

93

У меня есть файлы TypeScript:

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

Это дает мне ошибки при попытке использовать new

Модуль «MyClass» преобразуется в немодульную сущность и не может быть импортирован с помощью этой конструкции.

и при попытке позвонить fn()

Невозможно вызвать выражение, тип которого не имеет сигнатуры вызова.

Что дает?

Райан Кавано
источник
2
Спасибо, что поделились ответом. Я бы предложил удалить javascriptв качестве основного тега и оставить ecmascript-6, потому что здесь основной тег typescript. Вопрос ошибочно предполагает, что export =(функция TS) может быть связана с import ... from, в то время как она должна быть связана сimport = . Это в основном импорт / экспорт модуля ES6 по сравнению с CJS / AMD.
Estus Flask

Ответы:

158

Почему не работает

import * as MC from './MyClass';

Это importсинтаксис в стиле ES6 / ES2015 . Точное значение этого слова: «Возьмите загруженный объект пространства имен модуля ./MyClassи используйте его локально как MC». Примечательно, что « объект пространства имен модуля » состоит только из простого объекта со свойствами. Объект модуля ES6 не может быть вызван как функция или с new.

Для того, чтобы сказать это снова: ES6 объект модуля пространства имен не может быть вызван в качестве функции или с new.

Вещь, которую вы importиспользуете * as Xиз модуля, определяется только как имеющая свойства. В пониженном уровне CommonJS это может не соблюдаться полностью, но TypeScript сообщает вам, каково поведение, определенное стандартом.

Что работает?

Чтобы использовать этот модуль, вам нужно будет использовать синтаксис импорта в стиле CommonJS:

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

Если вы управляете обоими модулями, вы можете использовать export defaultвместо них:

MyClass.ts

export default class MyClass {
  constructor() {
  }
}

MyConsumer.ts

import MC from './MyClass';

Мне грустно об этом; Правила глупы.

Было бы неплохо использовать синтаксис импорта ES6, но теперь я должен это сделать import MC = require('./MyClass');? Наступил такой 2013 год! Ламе! Но горе - нормальная часть программирования. Перейдите к пятому этапу модели Кюблер-Росс: Принятие.

TypeScript здесь говорит вам, что это не работает, потому что это не работает. Есть хаки (добавление namespaceобъявления в MyClass- популярный способ притвориться, что это работает), и они могут работать сегодня в вашем конкретном сборщике модулей с понижением уровня (например, свертке), но это иллюзия. В мире пока нет реализаций модулей ES6, но так будет не всегда.

Представьте себя в будущем, пытаясь работать на аккуратной собственной реализации модуля ES6 и обнаруживая, что вы настроили себя на серьезную неудачу, пытаясь использовать синтаксис ES6 для выполнения чего-то, чего ES6 явно не делает .

Я хочу воспользоваться моим нестандартным загрузчиком модулей

Возможно, у вас есть загрузчик модулей, который «услужливо» создает defaultэкспорт, когда его нет. Я имею в виду, что люди создают стандарты по какой-то причине, но игнорировать стандарты иногда весело, и мы можем подумать, что это круто.

Измените MyConsumer.ts на:

import A from './a';

И укажите allowSyntheticDefaultImportsкомандную строку или tsconfig.jsonпараметр.

Обратите внимание, что это allowSyntheticDefaultImportsникак не меняет поведение вашего кода во время выполнения. Это просто флаг, который сообщает TypeScript, что ваш загрузчик модуля создает defaultэкспорт, когда его нет. Это не заставит ваш код работать в nodejs волшебным образом, если этого не было раньше.

Райан Кавано
источник
Разве для стиля commonjs не требуется цель commonjs? Есть ли способ сделать это, когда вы нацеливаетесь на es6 / es2015?
Steve Buzonas,
Вы не можете заставить его работать с таргетингом на ES6, потому что он не работает в ES6 ...
Райан Кавано,
Правильно ли я понимаю, что если я нацелен на модули ES2015, нет возможности ссылаться на модуль CommonJS, который имеет export = MyClass? Мой единственный вариант - установить мой модуль commonjsи продолжать делать мир хуже, не используя современные ES?
Мика Золту
6
В примечаниях к выпуску 2.7 , ниже --esModuleInterop, говорится: «Мы настоятельно рекомендуем применять его как к новым, так и к существующим проектам». На мой взгляд, этот ответ (и запись / политика в FAQ по DefinitelyTypedэтим ссылкам здесь) следует изменить, чтобы отразить новую позицию.
Alec Mev
1
Так очень нахально.
jmealy
25

TypeScript 2.7 представляет поддержку путем создания новых вспомогательных методов: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#support-for-import-d-from-cjs-form- commonjs-modules-with --- esmoduleinterop

Итак, в tsconfig.json добавьте эти две настройки:

{
    // 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
}

И теперь вы можете использовать:

import MyClass from './MyClass';
Майкл
источник
Вместо того, чтобы использовать, esModuleInteropя использовал resolveJsonModuleваше другое предложение об использовании, allowSyntheticDefaultImportsи это сработало для меня.
Эдгар Кинтеро
6

Добавьте сюда мои 2 цента, если у кого-то еще есть эта проблема.

Мой способ решения проблемы без изменения tsconfig.json(что может быть проблематичным в некоторых проектах), я просто отключил правило для oneline.

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

Шахар Хадас
источник
5

Я получил эту ошибку при попытке включить пакет противодействия npm в свой проект.

Когда я попробовал принятое выше решение, у меня возникло исключение:

Назначение импорта нельзя использовать при нацеливании на модули ECMAScript. Вместо этого рассмотрите возможность использования 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"' или другого формата модуля.

В итоге это сработало:

import debounce from 'debounce' 
NSjonas
источник