Hiểu esModuleInterop trong tệp tsconfig


142

Tôi đang kiểm tra .tsconfighồ sơ của ai đó và ở đó tôi phát hiện ra--esModuleInterop

Đây là .tsconfighồ sơ của anh ấy

{
  "compilerOptions": {
    "moduleResolution": "node",
    "target": "es6",
    "module": "commonjs",
    "lib": ["esnext"],
    "strict": true,
    "sourceMap": true,
    "declaration": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "declarationDir": "./dist",
    "outDir": "./dist",
    "typeRoots": ["node_modules/@types"]
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modues"]
}

Đây, câu hỏi chính của tôi là cái gì "esModuleInterop": true,"allowSyntheticDefaultImports": true,. Tôi biết họ phụ thuộc vào "module": "commonjs",. Ai đó có thể cố gắng giải thích nó bằng ngôn ngữ con người tốt nhất có thể không?

Các tài liệu chính thức cho allowSyntheticDefaultImportscác tiểu bang

Cho phép nhập mặc định từ các mô-đun không có xuất mặc định. Điều này không ảnh hưởng đến việc phát ra mã, chỉ là đánh máy.

Có nghĩa là? nếu không có bất kỳ mặc định xuất nào thì tôi nghĩ trường hợp sử dụng duy nhất của mặc định nhập sẽ là khởi tạo thứ gì đó? như singleton?

Câu hỏi / câu trả lời sau cũng không có ý nghĩa Có cách nào để sử dụng --esModuleInterop trong tsconfig thay vì nó là một lá cờ không?

--esModuleInteropđịnh nghĩa trên trang trình biên dịch

Phát ra các trình trợ giúp __importStar và __importDefault để tương thích với hệ sinh thái babel trong thời gian chạy và bật --allowSyntheticDefaultImports để tương thích với hệ thống kiểu chữ.

Cũng có vẻ khó hiểu đối với tôi

Câu trả lời:


169

Báo cáo vấn đề

Sự cố xảy ra khi chúng tôi muốn nhập mô-đun CommonJS vào cơ sở mã mô-đun ES6.

Trước những cờ này, chúng tôi phải nhập các mô-đun CommonJS với nhập sao ( * as something):

// node_modules/moment/index.js
exports = moment
// index.ts file in our app
import * as moment from 'moment'
moment(); // not compliant with es6 module spec

// transpiled js (simplified):
const moment = require("moment");
moment();

Chúng ta có thể thấy rằng *bằng cách nào đó tương đương với exportsbiến. Nó hoạt động tốt, nhưng nó không tuân thủ thông số kỹ thuật của mô-đun es6. Trong đặc tả, bản ghi không gian tên trong nhập sao ( momenttrong trường hợp của chúng tôi) chỉ có thể là một đối tượng thuần túy, không thể gọi được ( moment()không được phép).

Giải pháp

Với cờ, esModuleInteropchúng ta có thể nhập các mô-đun CommonJS tuân theo es6thông số kỹ thuật của mô-đun. Bây giờ mã nhập của chúng tôi trông giống như sau:

// index.ts file in our app
import moment from 'moment'
moment(); // compliant with es6 module spec

// transpiled js with esModuleInterop (simplified):
const moment = __importDefault(require('moment'));
moment.default();

Nó hoạt động và hoàn toàn hợp lệ với thông số mô-đun es6, vì momentkhông phải là không gian tên từ nhập sao, đó là nhập mặc định.

Nhưng nó hoạt động như thế nào? Như Bạn có thể thấy, bởi vì chúng tôi đã nhập mặc định, chúng tôi gọi thuộc defaulttính trên momentđối tượng. Nhưng chúng tôi đã không khai báo bất kỳ thuộc defaulttính nào trên exportsthư viện object in moment. Chìa khóa là ở __importDefaultchức năng. Nó gán module ( exports) cho thuộc defaulttính cho các module CommonJS:

var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};

Như Bạn có thể thấy, chúng tôi nhập các mô-đun es6 như hiện tại, nhưng các mô-đun CommonJS được bao bọc thành đối tượng bằng defaultkhóa. Điều này giúp bạn có thể nhập các giá trị mặc định trên các mô-đun CommonJS.

__importStarthực hiện công việc tương tự - nó trả về các esModules chưa được chạm, nhưng dịch các mô-đun CommonJS thành các mô-đun có thuộc defaulttính:

// index.ts file in our app
import * as moment from 'moment'

// transpiled js with esModuleInterop (simplified):
const moment = __importStar(require("moment"));
// note that "moment" is now uncallable - ts will report error!
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};

Nhập khẩu tổng hợp

Và những gì về allowSyntheticDefaultImports, nó là để làm gì? Bây giờ các tài liệu sẽ rõ ràng:

Allow default imports from modules with no default export. This does not affect code emit, just typechecking.

Trong các kiểu đánh máy, momentchúng tôi không chỉ định xuất mặc định và chúng tôi không nên có, vì nó chỉ khả dụng khi bật cờ esModuleInterop. Vì vậy, allowSyntheticDefaultImportschỉ sẽ không báo lỗi nếu chúng tôi muốn nhập mặc định từ mô-đun bên thrid không có xuất mặc định.


Trước sự ra đời của --esModuleInteroplá cờ, import * as moment from 'moment'không chính xác. import moment = require('moment')đã.
Aluan Haddad

14

esModuleInteroptạo các trình trợ giúp được nêu trong tài liệu. Nhìn vào mã đã tạo, chúng ta có thể thấy chính xác những gì chúng làm:

//ts 
import React from 'react'
//js 
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __importDefault(require("react"));

__importDefault: Nếu mô-đun không phải là một esmô-đun thì những gì được trả về bởi request sẽ trở thành mặc định. Điều này có nghĩa là nếu bạn sử dụng nhập mặc định trên một commonjsmô-đun, thì toàn bộ mô-đun thực sự là mặc định.

__importStarđược mô tả tốt nhất trong bài PR này :

TypeScript coi một nhập không gian tên (tức là import * as foo from "foo") tương đương với const foo = require("foo"). Mọi thứ rất đơn giản ở đây, nhưng chúng không hoạt động nếu đối tượng chính đang được nhập là một nguyên thủy hoặc một giá trị có chữ ký gọi / cấu trúc. ECMAScript về cơ bản nói rằng một bản ghi không gian tên là một đối tượng thuần túy.

Trước tiên Babel yêu cầu trong mô-đun và kiểm tra một thuộc tính có tên __esModule. Nếu __esModuleđược đặt thành true, thì hành vi giống như của TypeScript, nhưng ngược lại, nó tổng hợp một bản ghi không gian tên trong đó:

  1. Tất cả các thuộc tính được loại bỏ khỏi mô-đun yêu cầu và có sẵn dưới dạng nhập khẩu được đặt tên.
  2. Mô-đun yêu cầu ban đầu được cung cấp dưới dạng nhập mặc định.

Vì vậy, chúng tôi nhận được điều này:

// ts
import * as React from 'react'

// emitted js
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var React = __importStar(require("react"));

allowSyntheticDefaultImportslà bạn đồng hành với tất cả những điều này, việc đặt giá trị này thành false sẽ không thay đổi các trình trợ giúp đã phát ra (cả hai sẽ trông giống nhau). Nhưng nó sẽ phát sinh lỗi sắp chữ nếu bạn đang sử dụng nhập mặc định cho mô-đun commonjs. Vì vậy, điều này import React from 'react'sẽ làm tăng lỗi Module '".../node_modules/@types/react/index"' has no default export.nếu allowSyntheticDefaultImportsfalse.

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.