在使用 Typescript
时遇到个错误。
This syntax requires an imported helper but module 'tslib' cannot be found.ts(2354)
在一番搜索之下,也许你可以找到这样的解决方案:安装依赖 tslib
。
这固然可以解决问题,但我想我们心中的疑惑并没有解开
- 为什么许多项目并没有安装这个依赖却不会有这个报错呢
- 这个依赖到底是干啥用的
#tsconifg 之 importedHelper
也许我们可以很快的发现,该报错的罪魁祸首便是 tsconifg.compilerOptions
中的 importHelpers
。通过查阅文档可知,开启该选项后,一些降级操作会从 tslib
导入。
这里有两个关键词,降级与导入
#降级 - tsconfig 之 JavaScript 版本
我们通过仔细的观察可以发现,该报错总是出现在一些比较新的语法上面。而这里的新则是相对于 tsconfig.compilerOptions
中的 target
而言的。打个比方,我们 target
设置成了 ES5
,但我们的语法中却使用到了 ES6
或者甚至更新的语法,同时还开启了 importedHelper
,那该报错便会出现。
由上我们可知,我们一样可以通过提升 TS
的编译目标版本进而避免这个错误。当然,这样做的前提是我们知道,并保证使用代码的目标环境支持该版本的 JavaScript
。同时,这就是 为什么许多项目并没有安装这个依赖却不会有这个报错 的原因了。它们(没有报错的项目)要么是没有设置 importedHelper
,要么是编译目标使用较高版本 JavaScript
,TS
无需对语法进行降级(类似 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
这个依赖,我相信它能在一定程度上压缩整个项目的体积。