Apa yang dimaksud dengan "... memutuskan entitas non-modul dan tidak dapat diimpor menggunakan konstruksi ini"?

93

Saya memiliki beberapa file 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();

Ini memberi saya kesalahan saat mencoba menggunakan new

Modul "MyClass" menyelesaikan entitas non-modul dan tidak dapat diimpor menggunakan konstruksi ini.

dan saat mencoba menelepon fn()

Tak dapat memanggil ekspresi yang tipenya tidak memiliki tanda tangan panggilan.

Apa yang memberi?

Ryan Cavanaugh
sumber
2
Terima kasih telah membagikan jawaban. Saya akan menyarankan untuk menghapusnya javascriptsebagai tag utama dan keluar ecmascript-6, karena tag utama di sini adalah typescript. Pertanyaannya salah mengasumsikan bahwa export =(fitur TS) dapat dipasangkan import ... from, sementara itu harus dipasangkanimport = . Ini pada dasarnya adalah impor / ekspor modul ES6 vs CJS / AMD.
Estus Flask

Jawaban:

156

Mengapa tidak berhasil

import * as MC from './MyClass';

Ini adalah importsintaks gaya ES6 / ES2015 . Arti sebenarnya dari ini adalah "Ambil objek ruang nama modul yang dimuat dari ./MyClassdan gunakan secara lokal sebagai MC". Khususnya, " objek ruang nama modul " hanya terdiri dari objek biasa dengan properti. Objek modul ES6 tidak dapat dipanggil sebagai fungsi atau dengan new.

Untuk mengatakannya lagi: Objek namespace modul ES6 tidak dapat dipanggil sebagai fungsi atau dengan new.

Hal yang Anda importgunakan * as Xdari modul didefinisikan hanya memiliki properti. Dalam CommonJS downleveled ini mungkin tidak sepenuhnya dipatuhi, tetapi TypeScript memberi tahu Anda apa perilaku yang ditentukan oleh standar itu.

Apa yang berhasil?

Anda harus menggunakan sintaks impor gaya CommonJS untuk menggunakan modul ini:

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

Jika Anda mengontrol kedua modul, Anda dapat menggunakan export default:

MyClass.ts

export default class MyClass {
  constructor() {
  }
}

MyConsumer.ts

import MC from './MyClass';

Saya Sedih Tentang Ini; Aturannya Bodoh.

Alangkah baiknya menggunakan sintaks impor ES6, tetapi sekarang saya harus melakukan import MC = require('./MyClass');hal ini ? Ini sangat 2013! Kuno! Tetapi kesedihan adalah bagian normal dari pemrograman. Silakan lompat ke tahap lima dalam model Kübler-Ross: Penerimaan.

TypeScript di sini memberi tahu Anda bahwa ini tidak berfungsi, karena tidak berfungsi. Ada peretasan (menambahkan namespacedeklarasi ke MyClassadalah cara yang populer untuk menganggap ini berfungsi), dan mereka mungkin bekerja hari ini di bundler modul penurunan tingkat tertentu (misalnya rollup), tetapi ini ilusi. Belum ada implementasi modul ES6 di alam liar, tetapi itu tidak akan berlaku selamanya.

Bayangkan diri Anda di masa depan, mencoba menjalankan implementasi modul ES6 asli neato dan menemukan bahwa Anda telah menyiapkan diri Anda sendiri untuk kegagalan besar dengan mencoba menggunakan sintaks ES6 untuk melakukan sesuatu yang secara eksplisit tidak dilakukan ES6 .

Saya ingin memanfaatkan pemuat modul non-standar saya

Mungkin Anda memiliki pemuat modul yang "membantu" membuat defaultekspor jika tidak ada. Maksud saya, orang membuat standar karena suatu alasan, tetapi mengabaikan standar terkadang menyenangkan dan kami dapat berpikir itu hal yang keren untuk dilakukan.

Ubah MyConsumer.ts menjadi:

import A from './a';

Dan tentukan baris allowSyntheticDefaultImportsperintah atau tsconfig.jsonopsi.

Perhatikan bahwa allowSyntheticDefaultImportstidak mengubah perilaku waktu proses kode Anda sama sekali. Itu hanya sebuah bendera yang memberi tahu TypeScript bahwa pemuat modul Anda membuat defaultekspor ketika tidak ada. Ini tidak akan secara ajaib membuat kode Anda berfungsi di nodejs ketika sebelumnya tidak.

Ryan Cavanaugh
sumber
Bukankah gaya commonjs membutuhkan target commonjs? Adakah cara untuk membuat ini berfungsi ketika Anda menargetkan es6 / es2015?
Steve Buzonas
Anda tidak dapat membuatnya berfungsi dengan menargetkan ES6 karena tidak berfungsi di ES6 ...
Ryan Cavanaugh
Apakah saya benar dalam memahami bahwa jika saya menargetkan modul ES2015, tidak ada cara untuk mereferensikan modul CommonJS yang memilikinya export = MyClass? Satu-satunya pilihan saya adalah mengatur modul saya commonjsdan terus membuat dunia menjadi tempat yang lebih buruk dengan tidak menggunakan ES modern?
Mikha Zoltu
6
Dalam catatan rilis 2.7 , di bawah --esModuleInterop, dikatakan "Kami sangat menyarankan untuk menerapkannya baik ke proyek baru maupun yang sudah ada". Menurut pendapat saya, jawaban ini (dan entri / kebijakan FAQ di DefinitelyTypedtautan itu di sini) harus dimodifikasi untuk mencerminkan sikap baru.
Alec Mev
1
Sangat lancang.
jmealy
25

TypeScript 2.7 memperkenalkan dukungan dengan mengeluarkan metode pembantu baru: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#support-for-import-d-from-cjs-form- commonjs-modules-with --- esmoduleinterop

Jadi di tsconfig.json tambahkan dua pengaturan ini:

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

Dan sekarang Anda dapat menggunakan:

import MyClass from './MyClass';
Michael
sumber
Alih-alih menggunakan esModuleInteropsaya digunakan resolveJsonModuledi samping saran Anda yang lain untuk menggunakan allowSyntheticDefaultImportsdan itu berhasil untuk saya.
Edgar Quintero
6

Menambahkan 2 sen saya di sini jika orang lain mengalami masalah ini.

Cara saya mengatasi masalah ini tanpa memodifikasi tsconfig.json(yang dapat menjadi masalah di beberapa proyek), saya hanya menonaktifkan aturan untuk oneline.

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

Shahar Hadas
sumber
5

Saya mendapat kesalahan ini saat mencoba memasukkan paket npm debounce dalam proyek saya.

Ketika saya mencoba solusi yang diterima di atas, saya mendapat pengecualian:

Tugas impor tidak dapat digunakan saat menargetkan modul ECMAScript. Pertimbangkan untuk menggunakan 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', atau format modul lainnya.

Ini akhirnya berhasil:

import debounce from 'debounce' 
NSjonas
sumber