TypeScript: tslib library
Transfer. Original link .
In some cases, the compiler TypeScript
inserts helper functions into the generated JavaScript
code, which are then called at run time. Each such auxiliary function emulates the semantics of a language feature that is not yet natively supported by browsers.
Currently, the TypeScript
following helper functions exist:
__extends
for inheritance__assign
for thespread
operator__rest
for therest
operator__decorate
,__param
and__metadata
for decorators__awaiter
and__generator
forasync/await
For example, if the following code:
import * as React from "react";
export default class FooComponent extends React.Component<{}, {}> {
render() {
return (
Foo
);
}
}
compile into a standard ES5
that does not support either a class or an extension, the output will be:
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var React = require("react");
var FooComponent = (function (_super) {
__extends(FooComponent, _super);
function FooComponent() {
return _super.apply(this, arguments) || this;
}
FooComponent.prototype.render = function () {
return (React.createElement("div", null, "Foo"));
};
return FooComponent;
}(React.Component));
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = FooComponent;
This is only suitable for simple examples like the one described above. In fact, he has a huge flaw.
A helper function is __extends
inserted into the compiled result for each file in which you use class inheritance. This means that for each component React
based on the class, a helper function is inserted.
For a medium sized application that includes dozens or hundreds of React
components, this will result in a lot of repetitive code just for the function __extends
. This increases the size of the bundle, as a result of which the file download time increases.
This problem only gets worse when other helpers are used in the compiled result. The helper functions are __awaiter
both __generator
huge and make a significant contribution to the size of the bundle. Remember, they are inserted for each file in which you use async/await
.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments)).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t;
return { next: verb(0), "throw": verb(1), "return": verb(2) };
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [0, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Flag --noEmitHelpers
Starting with the version 1.5
, it TypeScript
supports the flag --noEmitHelpers
. If the compiler sees this flag, it TypeScript
will not insert helper functions in the compiled code.
React
The component described above compiled with the flag --noEmitHelpers
:
"use strict";
var React = require("react");
var FooComponent = (function (_super) {
__extends(FooComponent, _super);
function FooComponent() {
return _super.apply(this, arguments) || this;
}
FooComponent.prototype.render = function () {
return (React.createElement("div", null, "Foo"));
};
return FooComponent;
}(React.Component));
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = FooComponent;
Note that the function __extends
is still being called. In the end, the main thing is that the component React
works. If you use the flag --noEmitHelpers
, then it is your responsibility to provide all the necessary helper functions. TypeScript
suggests that they will be available at runtime.
However, it is too laborious to do this manually. You need to check what auxiliary functions are needed and then somehow make them available in the application. The outlook is far from bright! Fortunately, the team TypeScript
developed a solution.
Flag --importHelpers
and librarytslib
A 2.1
new flag has appeared in the version --importHelpers
that tells the compiler to import functions from an external library of helpers tslib
, rather than shred them for each file. You can install tslib
just like any other npm package:
npm install tslib --save
Let's recompile our React
component. This time using the flag --importHelpers
:
"use strict";
var tslib_1 = require("tslib");
var React = require("react");
var FooComponent = (function (_super) {
tslib_1.__extends(FooComponent, _super);
function FooComponent() {
return _super.apply(this, arguments) || this;
}
FooComponent.prototype.render = function () {
return (React.createElement("div", null, "Foo"));
};
return FooComponent;
}(React.Component));
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = FooComponent;
Pay attention to require("tslib")
the second line and use tslib_1.__extends
on the fifth. This file no longer has a helper function. Instead, the function is __extends
imported from the module tslib
. Thus, each helper is imported only once, and you will not be punished for the use extends
and async/await
in many files.