babel-loader / tsc 编译器如何知道在只为类型导入包时不导入包?

How does babel-loader / tsc compiler know NOT to import a package when it's being imported just for the types?

我目前正在我的项目中实施 Typescript。

而且我发现了一些奇怪的东西。

您可以在下面的 App.tsx 文件中看到,我必须 import firebase from "firebase/app" 才能访问 firebase.app.App 类型。

App.tsx

import React from "react";
import firebase from "firebase/app";

interface App_PROPS {
  firebase: firebase.app.App | null;
};

firebase.initializeApp({});

const App: React.FC<App_PROPS> = () => {
  return(
    <div>App</div>
  )
};

export default App;

而且我认为这会成为我项目中的一个错误,因为自从我进行 SSR 以来,导入 CANNOT 发生在我的服务器代码上,因为 firebase/app 是不打算在服务器上使用。这就是为什么firebase包被传下来的原因props,顺便说一下。

但令我惊讶的是,这个包根本没有被导入:

当我使用 babel-loader(通过 webpack)用 babel 转译它时,这就是我得到的:

App.js - 用 babel

转译
var _react = _interopRequireDefault(require("react"));

// (...) SOME OTHER CODE
// I WILL NOT POST THE FULL CODE HERE
// BUT "firebase/app" WAS NOT IMPORTED

另外,当我使用 tsc src/App.tsx

转译它时

App.js - 用 tsc

转译
"use strict";
exports.__esModule = true;
var react_1 = require("react");
;
var App = function () {
    return (<div>App</div>);
};
exports["default"] = App;

// "firebase/app" WAS NOT IMPORTED

有人可以向我解释一下这种情况下发生了什么吗?我很高兴转译器没有导入包,但这是为什么呢?

是因为我只使用 interface {} 中的包吗?因此,在为打字稿转译之后,该接口将消失(因为它不能存在于 JS 中)并且包将被某些 tree-shaking 算法丢弃?

OBS: 我也尝试了相同的代码,但这次调用了 firebase.initializeApp(),这是 firebase 包中的一个方法。在这种情况下,firebase/app 包被导入到 App.tsx 文件的转译版本中,这是有道理的。

这一切都归结为这个导入是否被用作一个值?或者只是键入?

我来解释一下:

以此为例:

import firebase from "firebase/app";

如果你使用firebase.initializeApp() - 你使用它的值,typescript编译生成的javascript需要提供initalizeApp函数并且有它 运行 在 运行 时间 - 编译结束后。这意味着它需要访问 firebase 导入,因此 typescript 编译 不会剪切 此导入,稍后 webpack 将其传输到 __ webpack__require.

另一方面,如果您使用 firebase: firebase.app.App | null; - ,您将使用它的类型 。 您是在对打字稿编译器说:“当您键入安全 firebase 属性 时,请确保其类型为 firebase.app.App。” - 完成此类型安全检查后,不再需要任何与 firebase 相关的内容。

当 typescript 编译一个文件时,它会检查它是否仅使用来自给定导入的类型。如果是,它会减少导入。


旁注:

在 Typescript 3.8 中 import type 引入了关键字。

它让你定义

import type firebase from "firebase/app";

这意味着此导入只能用于类型。如果您尝试 firebase.initializeApp() 构建将失败。 如果您担心未来的开发人员也使用此导入作为值,那将是您的场景的经典。