TypeScript: tslib library

Transfer. Original link .


In some cases, the compiler TypeScriptinserts helper functions into the generated JavaScriptcode, 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 TypeScriptfollowing helper functions exist:


  • __extends for inheritance
  • __assignfor the spreadoperator
  • __restfor the restoperator
  • __decorate, __paramand __metadatafor decorators
  • __awaiterand __generatorforasync/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 ES5that 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 __extendsinserted into the compiled result for each file in which you use class inheritance. This means that for each component Reactbased on the class, a helper function is inserted.


For a medium sized application that includes dozens or hundreds of Reactcomponents, 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 __awaiterboth __generatorhuge 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 TypeScriptsupports the flag --noEmitHelpers. If the compiler sees this flag, it TypeScriptwill not insert helper functions in the compiled code.


ReactThe 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 __extendsis still being called. In the end, the main thing is that the component Reactworks. If you use the flag --noEmitHelpers, then it is your responsibility to provide all the necessary helper functions. TypeScriptsuggests 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 TypeScriptdeveloped a solution.


Flag --importHelpersand librarytslib


A 2.1new flag has appeared in the version --importHelpersthat tells the compiler to import functions from an external library of helpers tslib, rather than shred them for each file. You can install tslibjust like any other npm package:


npm install tslib --save

Let's recompile our Reactcomponent. 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.__extendson the fifth. This file no longer has a helper function. Instead, the function is __extendsimported from the module tslib. Thus, each helper is imported only once, and you will not be punished for the use extendsand async/awaitin many files.


Also popular now: