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()
, 构建将失败。
如果您担心未来的开发人员也使用此导入作为值,那将是您的场景的经典。
我目前正在我的项目中实施 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()
, 构建将失败。
如果您担心未来的开发人员也使用此导入作为值,那将是您的场景的经典。