FXFX.GRID

tslib 是什么,你需要它吗

Typescript

在使用 Typescript 时遇到个错误。

This syntax requires an imported helper but module 'tslib' cannot be found.ts(2354)

在一番搜索之下,也许你可以找到这样的解决方案:安装依赖 tslib

这固然可以解决问题,但我想我们心中的疑惑并没有解开

  1. 为什么许多项目并没有安装这个依赖却不会有这个报错呢
  2. 这个依赖到底是干啥用的

#tsconifg 之 importedHelper

也许我们可以很快的发现,该报错的罪魁祸首便是 tsconifg.compilerOptions 中的 importHelpers。通过查阅文档可知,开启该选项后,一些降级操作会从 tslib 导入。

这里有两个关键词,降级与导入

#降级 - tsconfig 之 JavaScript 版本

我们通过仔细的观察可以发现,该报错总是出现在一些比较的语法上面。而这里的则是相对于 tsconfig.compilerOptions 中的 target 而言的。打个比方,我们 target 设置成了 ES5,但我们的语法中却使用到了 ES6 或者甚至更新的语法,同时还开启了 importedHelper,那该报错便会出现。

由上我们可知,我们一样可以通过提升 TS 的编译目标版本进而避免这个错误。当然,这样做的前提是我们知道,并保证使用代码的目标环境支持该版本的 JavaScript。同时,这就是 为什么许多项目并没有安装这个依赖却不会有这个报错 的原因了。它们(没有报错的项目)要么是没有设置 importedHelper ,要么是编译目标使用较高版本 JavaScriptTS 无需对语法进行降级(类似 Babel)。

#导入 - 压缩代码体积的有效手段

由选项 importedHelper 我们知道,假如我们在编译目标版本较低的项目中使用了新的语法,TS 是会自动帮我们做降级操作。

#实验 1

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES5"
  }
}
// a.ts
const a = ["name", "age"];
const b = [...a, "school"];
// a.out.js
"use strict";
var __spreadArray =
  (this && this.__spreadArray) ||
  function (to, from, pack) {
    if (pack || arguments.length === 2)
      for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
          if (!ar) ar = Array.prototype.slice.call(from, 0, i);
          ar[i] = from[i];
        }
      }
    return to.concat(ar || Array.prototype.slice.call(from));
  };
var a = ["name", "age"];
var b = __spreadArray(__spreadArray([], a, true), ["school"], false);

显然,TS 并没有骗我们,针对 ES6 中才原生支持的数组扩展符,在我们设置编译目标为 ES5 时,它进行了降级处理。

#实验 2

tsconfig 不变

// b.ts
const a = ["name", "age"];
const b = [...a, "school"];
export const nop = () => {};
// a.ts
import { nop } from "b";
nop();
const a = ["name", "age"];
const b = [...a, "school"];
// b.out.js
"use strict";
var __spreadArray =
  (this && this.__spreadArray) ||
  function (to, from, pack) {
    if (pack || arguments.length === 2)
      for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
          if (!ar) ar = Array.prototype.slice.call(from, 0, i);
          ar[i] = from[i];
        }
      }
    return to.concat(ar || Array.prototype.slice.call(from));
  };
Object.defineProperty(exports, "__esModule", { value: true });
exports.nop = void 0;
var a = ["name", "age"];
var b = __spreadArray(__spreadArray([], a, true), ["school"], false);
var nop = function () {};
exports.nop = nop;
// a.out.js
"use strict";
var __spreadArray =
  (this && this.__spreadArray) ||
  function (to, from, pack) {
    if (pack || arguments.length === 2)
      for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
          if (!ar) ar = Array.prototype.slice.call(from, 0, i);
          ar[i] = from[i];
        }
      }
    return to.concat(ar || Array.prototype.slice.call(from));
  };
Object.defineProperty(exports, "__esModule", { value: true });
var nop_1 = require("nop");
(0, nop_1.nop)();
var a = ["name", "age"];
var b = __spreadArray(__spreadArray([], a, true), ["school"], false);

我们在两个不同的模块中同时使用超越版本的语法,可以观察到 TS 为每个模块独立生成了降级代码。还记得 importedHelper 选项的作用吗?针对这些降级代码,它会从 tslib 导入。也就是说,它将不会再在每个模块生成降级代码。这就起到压缩代码量的作用。

#终语

至此,我相信你就完全明白 tslib 这个库的作用了。它便是把一系列的降级代码(函数)抽离并合并导出的库。目的是降低编译后代码的数量,起到压缩代码体积的作用。

#回答:你是否需要tslib

这首先取决与你运行目标代码的环境,假如运行环境支持更新版本的JavaScript,那我的建议是不妨把编译目标等级调高一点,从真正意义上减少代码的体积。而如果你的运行环境有明确的 JavaScript 版本需求,那我强烈建议你打开 importedHelper 这个选项,并安装 tslib 这个依赖,我相信它能在一定程度上压缩整个项目的体积。

FXFX.THEME