“… Phân giải thành một thực thể không phải mô-đun và không thể được nhập bằng cấu trúc này” nghĩa là gì?


93

Tôi có một số tệp 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();

Điều này khiến tôi gặp lỗi khi cố gắng sử dụng new

Mô-đun "MyClass" phân giải thành một thực thể không phải mô-đun và không thể được nhập bằng cấu trúc này.

và khi cố gắng gọi fn()

Không thể gọi một biểu thức có kiểu thiếu chữ ký cuộc gọi.

Đưa cái gì?


2
Cảm ơn đã chia sẻ một câu trả lời. Tôi khuyên bạn nên xóa javascriptlàm thẻ chính và bỏ đi ecmascript-6, bởi vì thẻ chính ở đây là typescript. Câu hỏi giả định sai rằng export =(một tính năng TS) có thể được ghép nối với import ... from, trong khi nó phải được ghép nối vớiimport = . Về cơ bản nó là nhập / xuất mô-đun ES6 so với CJS / AMD.
Estus Flask

Câu trả lời:


156

Tại sao nó không hoạt động

import * as MC from './MyClass';

Đây là importcú pháp kiểu ES6 / ES2015 . Ý nghĩa chính xác của điều này là "Lấy đối tượng không gian tên mô-đun được tải từ ./MyClassvà sử dụng nó cục bộ như MC". Đáng chú ý, " đối tượng không gian tên mô-đun " chỉ bao gồm một đối tượng thuần túy với các thuộc tính. Một đối tượng mô-đun ES6 không thể được gọi dưới dạng một hàm hoặc với new.

Nói lại lần nữa: Một đối tượng không gian tên mô-đun ES6 không thể được gọi dưới dạng một hàm hoặc với new.

Thứ bạn importsử dụng * as Xtừ một mô-đun được xác định là chỉ có thuộc tính. Trong CommonJS cấp thấp, điều này có thể không được tôn trọng đầy đủ, nhưng TypeScript cho bạn biết hành vi được định nghĩa bởi tiêu chuẩn là gì.

Những gì hoạt động?

Bạn sẽ cần sử dụng cú pháp nhập kiểu CommonJS để sử dụng mô-đun này:

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

Nếu bạn kiểm soát cả hai mô-đun, bạn có thể sử dụng export defaultthay thế:

MyClass.ts

export default class MyClass {
  constructor() {
  }
}

MyConsumer.ts

import MC from './MyClass';

Tôi Buồn Về Điều Này; Quy tắc là Dumb.

Sẽ rất tuyệt nếu sử dụng cú pháp nhập ES6, nhưng bây giờ tôi phải làm điều này import MC = require('./MyClass');? Đó là năm 2013! Què! Nhưng đau buồn là một phần bình thường của lập trình. Hãy chuyển sang giai đoạn năm trong mô hình Kübler-Ross: Chấp nhận.

TypeScript ở đây cho bạn biết điều này không hoạt động, bởi vì nó không hoạt động. Có những bản hack (thêm namespacekhai báo vào MyClasslà một cách phổ biến để giả vờ điều này hoạt động) và chúng có thể hoạt động ngày nay trong gói mô-đun cấp thấp cụ thể của bạn (ví dụ: cuộn lên), nhưng điều này là viển vông. Chưa có bất kỳ triển khai mô-đun ES6 nào trong tự nhiên, nhưng điều đó sẽ không đúng mãi mãi.

Hình dung bản thân trong tương lai của bạn, cố gắng chạy trên triển khai mô-đun ES6 gốc neato và nhận thấy rằng bạn đã tự thiết lập cho mình một thất bại lớn bằng cách cố gắng sử dụng cú pháp ES6 để làm điều gì đó mà ES6 rõ ràng không làm .

Tôi muốn tận dụng bộ tải mô-đun không chuẩn của mình

Có thể bạn có một trình tải mô-đun "hữu ích" tạo defaultxuất khi không có. Ý tôi là, mọi người đưa ra các tiêu chuẩn là có lý do, nhưng việc bỏ qua các tiêu chuẩn đôi khi rất thú vị và chúng ta có thể nghĩ đó là một điều tuyệt vời nên làm.

Thay đổi MyConsumer.ts thành:

import A from './a';

Và chỉ định dòng allowSyntheticDefaultImportslệnh hoặc tsconfig.jsontùy chọn.

Lưu ý rằng điều đó allowSyntheticDefaultImportshoàn toàn không thay đổi hành vi thời gian chạy của mã của bạn. Nó chỉ là một lá cờ cho TypeScript biết rằng trình nạp mô-đun của bạn tạo ra các bản defaultxuất khi không có. Nó sẽ không làm cho mã của bạn hoạt động trong nodejs một cách kỳ diệu như trước đây.


Kiểu commonjs không yêu cầu mục tiêu commonjs sao? Có cách nào để làm cho điều này hoạt động khi bạn đang nhắm mục tiêu es6 / es2015 không?
Steve Buzonas

Bạn không thể làm cho nó làm việc nhắm mục tiêu ES6 vì nó không làm việc trong ES6 ...
Ryan Cavanaugh

Tôi hiểu đúng rằng nếu tôi đang nhắm mục tiêu các mô-đun ES2015, thì không có cách nào để tham chiếu đến một mô-đun CommonJS có export = MyClass? Lựa chọn duy nhất của tôi là đặt mô-đun của tôi thành commonjsvà tiếp tục làm cho thế giới trở nên tồi tệ hơn bằng cách không sử dụng ES hiện đại?
Micah Zoltu

6
Trong ghi chú phát hành 2.7 , có ghi--esModuleInterop "Chúng tôi thực sự khuyên bạn nên áp dụng nó cho cả các dự án mới và hiện có". Theo ý kiến ​​của tôi, câu trả lời này (và mục Câu hỏi thường gặp / chính sách trong DefinitelyTypedcác liên kết ở đây) nên được sửa đổi để phản ánh lập trường mới.
Alec Mev

1
Vì vậy, rất hỗn xược.
jmealy

25

TypeScript 2.7 giới thiệu hỗ trợ bằng cách đưa ra các phương thức trợ giúp mới: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#support-for-import-d-from-cjs-form- commonjs-modules-with --- esmoduleinterop

Vì vậy, trong tsconfig.json, hãy thêm hai cài đặt sau:

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

Và bây giờ bạn có thể sử dụng:

import MyClass from './MyClass';

Thay vì sử dụng, esModuleInteroptôi đã sử dụng resolveJsonModulecùng với đề xuất sử dụng khác của bạn allowSyntheticDefaultImportsvà nó đã làm việc cho tôi.
Edgar Quintero

6

Thêm 2 xu của tôi ở đây trong trường hợp người khác có vấn đề này.

Cách của tôi để giải quyết vấn đề mà không cần sửa đổi tsconfig.json(có thể là vấn đề trong một số dự án), tôi chỉ đơn giản là vô hiệu hóa quy tắc cho một dòng.

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


5

Tôi gặp lỗi này khi cố gắng đưa gói gỡ lỗi npm vào dự án của mình.

Khi tôi thử giải pháp được chấp nhận ở trên, tôi nhận được một ngoại lệ:

Không thể sử dụng chỉ định nhập khi nhắm mục tiêu các mô-đun ECMAScript. Thay vào đó, hãy xem xét sử dụng 'import * as ns from "mod"', 'import {a} từ "mod"', 'import d từ "mod"' hoặc một định dạng mô-đun khác.

Điều này đã kết thúc hoạt động:

import debounce from 'debounce' 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.