ts下 import React from ‘react’ 和import * as React from 'react'的区别

2,384 阅读2分钟

import React from 'react' 和 import * as React from 'react' 的区别

创建一段测试代码:

import React from 'react';
console.log(React)

使用ts默认编译配置(ts在默认会将代码编译为es5):

"use strict";
exports.__esModule = true;
var react_1 = require("react");
console.log(react_1["default"]);

再看一下

import * as React from 'react'
console.log(React.useEffect)
"use strict";
exports.__esModule = true;
var react_1 = require("react");
console.log(react_1);

在默认情况下ts会将esm模块编译成commonjs

  • 对于 export default 的变量,TS 会将其放在 module.exports 的 default 属性上
  • 对于 export 的变量,TS 会将其放在 module.exports 对应变量名的属性上
  • 额外给 module.exports 增加一个 __esModule: true 的属性,用来告诉编译器,这本来是一个 esm 模块

这个时候我们再看一下npm包中react的导出

image.png

可以看到通过npm方式引用react时默认是以commonjs方式导出的,结合上面ts默认编译的规则,import React from 'react' 会从 exports.default 上去拿代码,显然此时default属性不存在commonjs模块中,因此会导致打印undefined;而import * as React from 'react' 则会把React作为为一个对象因此不会有问题。

esModuleInterop 和 allowSyntheticDefaultImports

上面的问题延伸一下,其实不仅仅是引入react,在esm中引入任何commonjs的模块在ts默认编译时都会有这样的问题,ts提供了esModuleInteropallowSyntheticDefaultImports 这两个配置来影响ts默认的解析。

allowSyntheticDefaultImports 是一个类型检查的配置,它会把 import 没有 exports.default 的报错忽略,如果你的target是es6加上这个配置就够了,但如果你的目标代码是es5仅仅加上这个还不行,还需要使用esModuleInterop,因为它才会改变tsc的编译产物:

// tsconfig.json

{
    "compilerOptions": {
      "module": "commonjs",
      "target": "es5",
      "esModuleInterop":true
    }
 }
 
// index.ts
import React from 'react';
console.log(React.useEffect)

// tsc产物
"use strict";
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"));
console.log(react_1.default.useEffect);

在加上esModuleInterop 之后编译产物多了一个_importDefault 辅助函数,而他的作用就是给module.exports 加上default 属性。

根据 ts官网的说明 开启esModuleInterop的同时也会默认开启allowSyntheticDefaultImports,因此更推荐直接加esModuleInterop

扩展

除了使用ts来编译ts,还可以使用babel来处理ts文件扩展阅读2而babel处理import时采用的方式和ts的esModuleInterop类似:

使用babel-cli(需要 Babel 7)执行: npx babel ./ --out-dir ./ --extensions ".ts" --presets babel-preset-es2015

'use strict';

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

console.log(_react2.default);

其中_interopRequireDefault辅助函数思路和ts是一致的。

总结

1.使用 import * as React from 'react' 引入
2.使用 import React from 'react' 配合 tsconfig esModuleInterop:true [推荐]