erik lieben

Erik Lieben software developer at Effectory and organizer at the dotnet.amsterdam meetup

Using tslib for TypeScript 2.1 with Aurelia to save bandwidth

Published , 5 min read (896 words)

The TypeScript release notes for version 2.1 contains an interesting section named ‘Support for external helpers library (tslib)’. This new feature allows you to use require.js to get the polyfills or helper functions that are normally generated by TypeScript in each file to support backward compatibility (using ESnext features supported by TypeScript, but not yet by all browsers).

Support for external helpers library (tslib) TypeScript injects a handful of helper functions such as _extends for ?
inheritance, _assign for spread operator in object literals and JSX elements, and __awaiter for async functions.

Previously there were two options:

inject helpers in every file that needs them, or no helpers at all with — noEmitHelpers.

The two options left more to be desired; bundling the helpers in every file was a pain point for customers trying to keep ?
their package size small. And not including helpers, meant customers had to maintain their own helper’s library.
TypeScript 2.1 allows for including these files in your project once in a separate module, and the compiler will emit
imports to them as needed.

What’s the difference? #

If we take a very basic class written in Aurelia with some ES features that are currently not supported in all browsers:

typescript
import { bindable } from 'aurelia-framework';
export class App {
@bindable()
public message = 'Hello World!';
}
typescript
import { bindable } from 'aurelia-framework';
export class App {
@bindable()
public message = 'Hello World!';
}

TypeScript will generate some helper methods (to make sure it works everywhere) in the outputted JavaScript (which will be repeated for every class inside your bundle, because TypeScript isn’t aware of your bundling system):

javascript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); };
javascript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); };

above the code of the actual class in the ES5 amd format you transpiled to:

typescript
define('app',["require", "exports", "aurelia-framework"], function (require, exports, aurelia_framework_1) {
"use strict";
var App = (function () { function App() { this.message = 'Hello World!'; } return App; }());
__decorate([ aurelia_framework_1.bindable(), __metadata("design:type", Object) ], App.prototype, "message", void 0);
exports.App = App;
});
typescript
define('app',["require", "exports", "aurelia-framework"], function (require, exports, aurelia_framework_1) {
"use strict";
var App = (function () { function App() { this.message = 'Hello World!'; } return App; }());
__decorate([ aurelia_framework_1.bindable(), __metadata("design:type", Object) ], App.prototype, "message", void 0);
exports.App = App;
});

When the tslib option is enabled it will transpile to the following code:

typescript
define('app',["require", "exports", "tslib", "aurelia-framework"], function (require, exports, tslib_1, aurelia_framework_1) {
"use strict";
var App = (function () { function App() { this.message = 'Hello World!'; } return App; }());
tslib_1.__decorate([ aurelia_framework_1.bindable(), tslib_1.__metadata("design:type", Object) ], App.prototype, "message", void 0); exports.App = App; });
typescript
define('app',["require", "exports", "tslib", "aurelia-framework"], function (require, exports, tslib_1, aurelia_framework_1) {
"use strict";
var App = (function () { function App() { this.message = 'Hello World!'; } return App; }());
tslib_1.__decorate([ aurelia_framework_1.bindable(), tslib_1.__metadata("design:type", Object) ], App.prototype, "message", void 0); exports.App = App; });

As you can probably imagine, this will result in a lot less JavaScript code when you have a decent sized application (because it only needs to transfer(have) the helper/ polyfill once in the tslib package).

See it in action #

We will add this to a new Aurelia TypeScript project generated by the Aurelia-CLI.

Aurelia TypeScript project #

First, create a default Aurelia project with the Aurelia-CLI by using the command:

bash
au new
bash
au new

And use the option Default TypeScript to generate the default configuration for a TypeScript project.

Adding the tslib dependency #

We can install the tslib dependency by performing the following command:

bash
npm i tslib -S
bash
npm i tslib -S

Setup tslib #

Next, we will modify the compiler options, in the tsconfig.json file to enable this feature to work. Add the following option:

json
{ "compilerOptions": { "importHelpers": true, .... } }
json
{ "compilerOptions": { "importHelpers": true, .... } }

Open the aurelia.json file, located in the aurelia_project folder, and add the dependency "tslib" on top.

javascript
"name": "vendor-bundle.js",
"prepend": [
"node_modules/bluebird/js/browser/bluebird.core.js",
"node_modules/requirejs/require.js"
],
"dependencies": [ "tslib", "aurelia-binding",
javascript
"name": "vendor-bundle.js",
"prepend": [
"node_modules/bluebird/js/browser/bluebird.core.js",
"node_modules/requirejs/require.js"
],
"dependencies": [ "tslib", "aurelia-binding",

Once you build your project now, it will automatically generate the require imports instead of the helper/ polyfill code.

Enjoy!