tsconfig.json中allowSyntheticDefaultImports和module的用法

1,116 阅读3分钟

一般开发用脚手架的话tsconfig.json已经帮我们默认生成好了,不需要去手动配置,所以平时很少去关心这里面配置都代表啥含义,比如我们经常写的一个导入import React from 'react', react默认是commonjs规范导出,那这样写为啥ts中不报错呢?其实这是因为allowSyntheticDefaultImports设置为true的缘故。接下来做一些测试了解下module,allowSyntheticDefaultImports还有esModuleInterop配置都是什么意思。

安装pnpm add typescript @types/node react @types/react

package.json

{
  "name": "final-ts-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "tsc": "tsc --build"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@types/node": "^18.11.9",
    "@types/react": "^18.0.25",
    "react": "^18.2.0",
    "typescript": "^4.8.4"
  }
}

准备三个文件,分别是esm.ts, cjs.ts, index.ts

cjs.ts

module.exports = {
  cjsA:1,
  cjsB:2
}

esm.ts

export default { 
  esmA: 1, 
  esmB: 2 
};
export const esmC = 3;

index.ts

/** 注释掉import {a} from './cjs'是因为会报ts错,自己写的commonjs的模块没有ts声明文件,
那么用es6的方式导入会报ts错误,  底下react没报错是因为我们引入了@types/react
*/
// import {a} from './cjs'
import esmDefault from './esm'
import * as esAll from './esm'
import { esmC } from './esm'
import React from 'react';

"a".includes("a");
Promise.resolve(4)

console.log(esmDefault) // import default export of esm
console.log(esAll) // import all the export include default from esm
console.log(esmC) // import the varibale from esm
console.log(React);

创建tsconfig.json文件

npx tsc --init

tsconfig.json


{
  "compilerOptions": {
    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "module": "commonjs",                                /* Specify what module code is generated. */
    "outDir": "./dist",                                   /* Specify an output folder for all emitted files. */
    "esModuleInterop": false,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */
    "strict": true,                                      /* Enable all strict type-checking options. */
    "skipLibCheck": true,                                 /* Skip type checking all .d.ts files. */
    "allowSyntheticDefaultImports": true,
  },
  "include": ["src"],
  "exclude": [
    "node_modules"
  ]
}

最终的目录结构如下:

image.png

接下来观察下tsconfig.json中的配置。

module: 设置为commonjs,代表我们希望tsc编译出来的js代码符合commonjs规范,如果设置为esnext,则说明我们希望tsc编译出来的结果符合esmodule规范(注意: commonjs方式写的模块依然会原样输出,不会被转为esmodule)

allowSyntheticDefaultImports:这个字段的作用只是在静态类型检查时,把 import 没有 exports.default 的报错忽略掉。就比如我们代码中写的import React from 'react' 如果不设置这个字段为true就会报错

image.png 点进去React会看到

image.png

esModuleInterop: 会真正的在编译的过程中生成兼容代码,使模块能正确的导入(这个只会在module:commonjs模式下生效)

接下来咱们在上面tsconfig.json的基础上来互相组合这几个配置来做个测试执行npm run tsc

{
    "module": "esnext",  
    "esModuleInterop": true, //这里设置为true或者false对tsc编译的结果没有区别                             
    "allowSyntheticDefaultImports": true// 一定要设置为true,不然import React from 'react'会报错
}

tsc编译结果原样输出,没有做任何更新 image.png

接下来测试

{
    "module": "commonjs",  
    "esModuleInterop": true,                            
    "allowSyntheticDefaultImports": true
}

可以看到esm.js和index.js相比原文件都有了很大的不同

image.png

esm.js这个结果跟babel编译后的代码很像

  1. 设置exports.__esModule = true, 代表源文件是esModule
  2. 设置exports.default = export default xx
  3. 设置export.xx = export const xx

index.js这个结果其实跟babel编译后的结果也大同小异

  1. import * as sth from 'library' = _importStar
  2. import sth from 'library' = _importDefault

image.png

接下来测试

{
    "module": "commonjs",  
    "esModuleInterop": false,                            
    "allowSyntheticDefaultImports": true
}

image.png 可以看到没有引入一些helper函数,只是设置了exports.__esModule = true

最后:

一般我们跟webpack结合使用时,将tsconfig.json设置为module:esnext, 就是为了不让typescript处理我们的代码增加一些工具函数方法(就算是设置了esModuleInterop:true也没用,因为moudle:esnext模式下不生效)