当前位置:   article > 正文

Typescript配置文件(tsconfig.json)详解系列四:esModuleInterop和allowSyntheticDefaultImports_tsconfig 配置esmodule

tsconfig 配置esmodule

Typescript版本

Typescript5.5.2

如果我们使用ESM作为模块系统,那么我们经常会用以下两种方式去导入另一个模块:

  1. // 引入一个对象,包括了所有的export xxx 和 export default
  2. import * as A from './xx';
  3. // 引入export default
  4. import B from './xx';

转换为Commonjs的写法为:

  1. const A = require('xx');
  2. const B = require('xx').default;

但是实际上这种转换并不是完全对等的,因为Commonjs中并没有default(实际上大部分模块系统都没有default)。这种不对等会导致Typescript编译后的js文件无法正常执行。

问题:import X from 'xx'

我们将esModuleInterop设置为false来模拟一下这些问题

  1. {
  2. compilerOptions: {
  3. "esModuleInterop": false
  4. }
  5. }

有如下两个模块分别使用ESM和Commonjs进行导出:

  1. // cjs_default.ts
  2. // Typescript的Commonjs导出写法
  3. export = {
  4. name: 1
  5. }
  6. // esm_default.ts
  7. // ESM导出
  8. export default {
  9. name: 'name'
  10. }

然后我们使用ESM来引入这两个模块

  1. import esm from './esm_default';
  2. import cjs from './cjs_default';
  3. console.log(cjs, esm);

此时编译会报错,原因就是我们之前提到的Commonjs没有默认导出:

  1. ts/index.ts:2:8 - error TS1259: Module '"C:/Users/86159/WebstormProjects/\u9762\u8BD5\u9898/ts/cjs_default"' can only be default-imported using the 'esModuleInterop' flag
  2. 2 import cjs from './cjs_default';
  3. ~~~
  4. ts/cjs_default.ts:1:1
  5. 1 export = {
  6. ~~~~~~~~~~
  7. 2 name: 1
  8. ~~~~~~~~~~~
  9. 3 }
  10. ~
  11. This module is declared with 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.
  12. Found 1 error in ts/index.ts:2
  13. Process finished with exit code 2

这个报错虽然让我们去开启esModuleInterop,但是实际上我们只需要将allowSyntheticDefaultImports这个字段设置为true就好了。

现在代码可以正常编译了:

  1. // cjs_default.js
  2. // 被正常编译为commonjs模块了
  3. module.exports = {
  4. name: 1
  5. };
  6. // esm_default.js
  7. // 这个exports.__esModule 标识了这个commonjs模块是由ESM模块编译转换的。
  8. Object.defineProperty(exports, "__esModule", { value: true });
  9. // module.exports可以省略module
  10. exports.default = {
  11. name: 'name'
  12. };
  13. // index.js
  14. "use strict";
  15. Object.defineProperty(exports, "__esModule", { value: true });
  16. var esm_default_1 = require("./esm_default");
  17. var cjs_default_1 = require("./cjs_default");
  18. console.log(cjs_default_1.default, esm_default_1.default);

我们执行一下生成的js代码,但是我们并没有正常的从Commonjs导出的模块中得到正确的结果(获得了undefined),虽然我们的类型检查通过了,代码也编译了,但是并没有编译成我们期望的结果。

undefined { name: 'name' }

我们将esModuleInterop设置为true之后代码可以正常编译了,我们看一下编译后的文件:

  1. // cjs_default.js
  2. // 被正常编译为commonjs模块了
  3. module.exports = {
  4. name: 1
  5. };
  6. // esm_default.js
  7. // 这个exports.__esModule 标识了这个commonjs模块是由ESM模块编译转换的。
  8. Object.defineProperty(exports, "__esModule", { value: true });
  9. // module.exports可以省略module
  10. exports.default = {
  11. name: 'name'
  12. };
  13. // index.js
  14. // 开启esModuleInterop为true会在编译文件中生成这个函数,如果在转化import A from 'xx'时调用。
  15. // 对没有default的Commonjs模块系统封装一层(通过有没有__esModule字段判断需不需要封装)
  16. var __importDefault = (this && this.__importDefault) || function (mod) {
  17. return (mod && mod.__esModule) ? mod : { "default": mod };
  18. };
  19. Object.defineProperty(exports, "__esModule", { value: true });
  20. var esm_default_1 = __importDefault(require("./esm_default"));
  21. var cjs_default_1 = __importDefault(require("./cjs_default"));
  22. console.log(cjs_default_1.default, esm_default_1.default);

 我们执行一下生成的js代码,这次可以正确的输出了:

{ name: 1 } { name: 'name' }

问题:import * as X from 'xx'

  1. {
  2. compilerOptions: {
  3. "esModuleInterop": true,
  4. "allowSyntheticDefaultImports": true,
  5. }
  6. }

index.ts代码如下:

  1. import * as esm from './esm_default';
  2. import * as cjs from './cjs_default';
  3. console.log(cjs, esm);

 编译一直报类型检查的错误,报错如下:

  1. ts/index.ts:2:22 - error TS2497: This module can only be referenced with ECMAScript imports/exports by turning on the 'esModuleInterop' flag and referencing its default export.
  2. 2 import * as cjs from './cjs_default';
  3. ~~~~~~~~~~~~~~~
  4. Found 1 error in ts/index.ts:2

 但是编译后的代码是可以执行的,执行结果如下,感觉还是有点奇怪的:

{ name: [Getter], default: { name: 1 } } { default: { name: 'name' } }

 编译后的代码可以看到,也是注入了转换函数(__importStar 等

  1. "use strict";
  2. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  3. if (k2 === undefined) k2 = k;
  4. var desc = Object.getOwnPropertyDescriptor(m, k);
  5. if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
  6. desc = { enumerable: true, get: function() { return m[k]; } };
  7. }
  8. Object.defineProperty(o, k2, desc);
  9. }) : (function(o, m, k, k2) {
  10. if (k2 === undefined) k2 = k;
  11. o[k2] = m[k];
  12. }));
  13. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  14. Object.defineProperty(o, "default", { enumerable: true, value: v });
  15. }) : function(o, v) {
  16. o["default"] = v;
  17. });
  18. var __importStar = (this && this.__importStar) || function (mod) {
  19. if (mod && mod.__esModule) return mod;
  20. var result = {};
  21. if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  22. __setModuleDefault(result, mod);
  23. return result;
  24. };
  25. Object.defineProperty(exports, "__esModule", { value: true });
  26. const esm = __importStar(require("./esm_default"));
  27. const cjs = __importStar(require("./cjs_default"));
  28. console.log(cjs, esm);

问题:import {x} from 'xx'

index.ts代码如下:

  1. import {
  2. name
  3. } from './cjs_default';
  4. console.log(name);

会报和import * as X from 'xx'一模一样的错误, 但是编译后的代码是可以执行的,执行结果如下:

  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const cjs_default_1 = require("./cjs_default");
  4. console.log(cjs_default_1.name);

 编译后的代码如下:

总结

allowSyntheticDefaultImports

  • allowSyntheticDefaultImports可以让这种使用ESM模块系统导入default时,目标模块系统没有default的类型检查通过(只影响类型检查,不影响编译结果)

esModuleInterop

  • esModuleInterop设置为true的时候,allowSyntheticDefaultImports如果没有定义,会自动跟着设置为true。
  • esModuleInterop会自动注入工具函数,将没有default的模块系统封装一层,让使用ESM模块系统导入default的结果符合预期。

使用ESM模块系统引入Commonjs模块

方式是否支持备注
import X from 'xx'支持
import * as X from 'xx'不支持报错,能正常编译
import {x} from 'xx'不支持报错,能正常编译

import * as X from 'xx',这种还有工具函数__importStar ,而且官方文档对esModuleInterop的讲解还举了这个例子但是会类型报错很不理解

如果想解决这个问题有几个方案:

1. 使用ts的校验错误忽略@ts-ignore

2. 改写export = 为 export,使用ESM模块系统

  1. export = {
  2. name: 1
  3. }
  4. // 改写为
  5. const name = 1;
  6. export {
  7. name
  8. }
  9. // 或者改写为
  10. export default {
  11. name
  12. }
  13. // 或者改写为
  14. export const name = 1;

3. 如果是引入的是js文件,那可以通过定义d.ts文件避免类型检验错误(虽然js中使用的是commonjs模块系统,但是d.ts定义成ESM模块系统)

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/903713
推荐阅读
相关标签
  

闽ICP备14008679号