Co oznacza „… odpowiada jednostce niebędącej modułem i nie można jej zaimportować przy użyciu tej konstrukcji”?

93

Mam kilka plików 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();

To daje mi błędy podczas próby użycia new

Moduł „MyClass” jest rozpoznawany jako element niebędący modułem i nie można go zaimportować przy użyciu tej konstrukcji.

i kiedy próbujesz zadzwonić fn()

Nie można wywołać wyrażenia, którego typ nie ma podpisu wywołania.

Co daje?

Ryan Cavanaugh
źródło
2
Dzięki za udzielenie odpowiedzi. Sugerowałbym usunięcie javascriptjako podstawowego tagu i pozostawienie go ecmascript-6, ponieważ podstawowy tag tutaj jest typescript. Pytanie błędnie zakłada, że export =(funkcja TS) może być sparowana import ... from, podczas gdy powinna być sparowanaimport = . Jest to w zasadzie import / eksport modułu ES6 vs CJS / AMD.
Estus Flask

Odpowiedzi:

158

Dlaczego to nie działa

import * as MC from './MyClass';

To jest importskładnia w stylu ES6 / ES2015 . Dokładne znaczenie tego to „ Pobierz ładowany obiekt przestrzeni nazw modułu ./MyClassi używaj go lokalnie jako MC”. Warto zauważyć, że „ obiekt przestrzeni nazw modułu ” składa się tylko z prostego obiektu z właściwościami. Obiekt modułu ES6 nie może być wywoływany jako funkcja lub z new.

Powiedzieć to jeszcze raz: ES6 nazw moduł obiekt nie może być powoływane jako funkcji lub new.

To, czego importużywasz * as Xz modułu, ma tylko właściwości. W CommonJS na niższym poziomie może to nie być w pełni przestrzegane, ale TypeScript mówi ci, jakie jest zachowanie zdefiniowane przez standard.

Co działa?

Aby użyć tego modułu, musisz użyć składni importu w stylu CommonJS:

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

Jeśli kontrolujesz oba moduły, możesz export defaultzamiast tego użyć :

MyClass.ts

export default class MyClass {
  constructor() {
  }
}

MyConsumer.ts

import MC from './MyClass';

Jestem z tego powodu smutny; Zasady są głupie.

Byłoby miło użyć składni importu ES6, ale teraz muszę to zrobić import MC = require('./MyClass');? Tak jest w 2013 roku! Kulawy! Ale żal jest normalną częścią programowania. Proszę przejść do etapu piątego w modelu Kübler-Ross: Akceptacja.

TypeScript mówi, że to nie działa, ponieważ nie działa. Istnieją hacki (dodawanie namespacedeklaracji do MyClassjest popularnym sposobem udawania, że ​​to działa) i mogą one działać dzisiaj w twoim konkretnym pakiecie modułów obniżających poziom (np. Rollup), ale jest to iluzoryczne. Nie ma jeszcze żadnych implementacji modułów ES6 na wolności, ale nie będzie to wieczne.

Wyobraź sobie swoje przyszłe ja, próbując uruchomić na neato natywnej implementacji modułu ES6 i stwierdzając, że naraziłeś się na poważną awarię, próbując użyć składni ES6 do zrobienia czegoś, czego ES6 wyraźnie nie robi .

Chcę skorzystać z mojego niestandardowego modułu ładującego

Może masz moduł ładujący, który „pomocnie” tworzy defaulteksport, gdy żaden nie istnieje. Chodzi mi o to, że ludzie tworzą standardy z jakiegoś powodu, ale ignorowanie standardów jest czasami zabawne i możemy pomyśleć, że to fajna rzecz.

Zmień MyConsumer.ts na:

import A from './a';

I określ allowSyntheticDefaultImportswiersz polecenia lub tsconfig.jsonopcję.

Zwróć uwagę, że allowSyntheticDefaultImportsnie zmienia to w ogóle zachowania kodu w czasie wykonywania. To tylko flaga, która mówi TypeScript, że moduł ładujący moduł tworzy defaulteksport, gdy żaden nie istnieje. Nie sprawi, że Twój kod będzie działał w nodejs w magiczny sposób, jak wcześniej.

Ryan Cavanaugh
źródło
Czy styl commonjs nie wymaga celu commonjs? Czy istnieje sposób, aby to zadziałało, gdy celujesz w es6 / es2015?
Steve Buzonas
Nie możesz sprawić, by działało na ES6, ponieważ nie działa w ES6 ...
Ryan Cavanaugh
Czy mam rację rozumiejąc, że jeśli jestem ukierunkowany na moduły ES2015, nie ma możliwości odniesienia się do modułu CommonJS, który ma export = MyClass? Moją jedyną opcją jest ustawienie mojego modułu commonjsi dalsze czynienie świata gorszym miejscem, nie używając nowoczesnego ES?
Micah Zoltu
6
W uwagach do wydania 2.7 pod --esModuleInteropnagłówkiem jest napisane „Zdecydowanie zalecamy stosowanie go zarówno do nowych, jak i istniejących projektów”. Moim zdaniem ta odpowiedź (i wpis / polityka FAQ w DefinitelyTypedtych linkach) powinna zostać zmodyfikowana, aby odzwierciedlić nowe stanowisko.
Alec Mev
1
Tak bardzo bezczelny.
jmealy
25

TypeScript 2.7 wprowadza obsługę, emitując nowe metody pomocnicze: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#support-for-import-d-from-cjs-form- commonjs-modules-with --- esmoduleinterop

Więc w tsconfig.json dodaj te dwa ustawienia:

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

A teraz możesz użyć:

import MyClass from './MyClass';
Michał
źródło
Zamiast używać esModuleInterop, użyłem resolveJsonModulerazem z twoją inną sugestią użycia allowSyntheticDefaultImportsi zadziałało to dla mnie.
Edgar Quintero
6

Dodanie moich 2 centów tutaj na wypadek, gdyby ktoś inny miał ten problem.

Mój sposób na obejście problemu bez modyfikowania tsconfig.json(co może być problematyczne w niektórych projektach), po prostu wyłączyłem regułę dla oneline.

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

Shahar Hadas
źródło
5

Wystąpił ten błąd podczas próby dołączenia pakietu debounce npm do mojego projektu.

Kiedy wypróbowałem powyższe rozwiązanie, dostałem wyjątek:

Nie można użyć przypisania importu w przypadku kierowania na moduły ECMAScript. Rozważ użycie opcji „import * as ns from„ mod ””, „import {a} from„ mod ””, „import d from„ mod ”” lub innego formatu modułu.

To się skończyło:

import debounce from 'debounce' 
NSjonas
źródło