Original issue: https://github.com/systemjs/builder/issues/178
@vladima is this issue is going to cover having some kind of runtime file with all the helpers? Currently using the noEmitHelpers: true flag will remove emitting helpers (like __decorate, __metadata etc.) from individual files. Still it would be great to have a file (as part of the TS npm distribution) that includes all those helpers.
Currently I'm hand-crafting this file on my end by moving generated helpers functions to one file checked-in in my project repo. Is there is a better way of doing this today? Or is it going to be covered by this issue?
Is this issue still needed?
If helpers functions are emitted right before a System.register call it prevents this module from being loaded from SystemJS. As it seems to be a valid use case it would be nice to have some config option to prevent that
the helpers emit have been moved in 1.6 to inside the System.register call. so
export class B {}
export class C extends B {}
emits:
System.register([], function(exports_1) {
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 B, C;
return {
setters:[],
execute: function() {
B = (function () {
function B() {
}
return B;
})();
exports_1("B", B);
C = (function (_super) {
__extends(C, _super);
function C() {
_super.apply(this, arguments);
}
return C;
})(B);
exports_1("C", C);
}
}
});
I chatted with @vladima offline, this issue is tracking moving the helpers into a module, and alloing the compiler to emit imports to that module, e.g. the above code would emit:
System.register(["ts_helpers"], function(exports_1) {
var __extends;
var B, C;
return {
setters:[function(ts_helpers) {
__extends = ts_helpers.__extends;
}],
execute: function() {
B = (function () {
function B() {
}
return B;
})();
exports_1("B", B);
C = (function (_super) {
__extends(C, _super);
function C() {
_super.apply(this, arguments);
}
return C;
})(B);
exports_1("C", C);
}
}
});
As for now, where can I find a full list of all these helpers?
I use noEmitHelpers to substitute __awaiter with my own implementation, but also I must know what else I may break with this option.
Right now, the only full list is in src/compiler/emitter.ts. We've been discussing releasing a separate script/library with all of our emit helpers.
If I may ask, what was the reason you chose to use your own implementation of __awaiter? Is there something deficient with the current implementation?
I'm experimenting with performance optimizations in Angular 1.x apps.
Angular has two implementations of promises with different behaviour: $q and $$q. They both are compatible with Promises/A+ and (mostly) with ES6. The difference is in how they schedule the execution of their callbacks. As for $q, the callbacks are executed during Angular's digest cycle. $$q works like normal ES6 promises. Basically, $$q is an optimization, which is used for async operations that shouldn't affect any Angular bindings, so there is no point in triggering the dirty checking (AKA digest) after they're done.
What I wanted to achieve is to avoid the unneeded converting of $$q promises to $q promises, which happened if I just did Promise = $q; and used the default implementation of __awaiter. So I replaced
function cast(value) {
return value instanceof Promise && value.constructor === Promise ?
value : new Promise(function (resolve) { resolve(value); });
}
with
function cast(value) {
return value && typeof value.then === 'function' ? value : Promise.resolve(value);
}
instanceof doesn't work for Angular's promises (angular/angular.js#13545), so I resorted to such an unreliable (but good enough for me) check.
IIRC, in Angular 1.x you get $q and $$q via dependency injection, and there is no Promise available globally or via a module import. You could always try:
// assumes $q and $$q are available in current lexical scope
type $q<T> = Promise<T>;
type $$q<T> = Promise<T>;
async function fn1(): $q<number> {
await sleep(100);
return 1;
}
async function fn2(): $$q<number> {
await sleep(100);
return 2;
}
For an async function, the return type annotation must be both a _type_ and a _value_ in the same scope. By creating a type alias for $q and $$q, you _should_ be able to switch between them. I haven't tested this yet as I'm not terribly familiar with Angular 1.x.
Yes, this magic works. :sparkles: Is it documented?
That's how I initialize Promise, $q and $$q globals now:
var Promise: PromiseConstructor;
var $q: ng.IQService;
var $$q: ng.IQService;
type $q<T> = Promise<T>;
type $$q<T> = Promise<T>;
angular.module('app').run(['$q', '$$q', function($q_, $$q_) {
Promise = $q_;
$q = $q_;
$$q = $$q_;
}]);
However, the default implementation of __awaiter still doesn't allow mixing different kinds of promises inside one async function without unneeded conversions. E.g., the result of the call to fn2 gets converted to $q here:
async function fn3(): $q<string> {
await fn1();
await fn2();
await fn1();
return 'fn3';
}
Ideally, transpiling something like the class below
export class A extends B {}
should yield something like this when targeting ES5 with ES6 modules.
import { __extends } from 'typescript-helpers';
export var A = (function (_super) {
__extends(A, _super);
function A() {
_super.apply(this, arguments);
}
return A;
})(B);
Targeting CommonJS modules might yield something like
var typescriptHelpers = require('typescript-helpers');
var A = (function (_super) {
typescriptHelpers. __extends(A, _super);
function A() {
_super.apply(this, arguments);
}
return A;
})(B);
exports.A = A;
How difficult would this be to implement?
When you pass TypeScript code through the Closure compiler, the underscore prefix on these names prevents Closure renaming of the function names. I guess if they're pulled from a namespaced module you'd no longer need the underscores.
(Edit: moved this into issue #7345.)
I really like the idea @Victorystick proposed. That would be a really neat way to handle it.
In my case I do want the helpers, I just don't want them respecified in every file they're needed. I'm sure I'm one of many with that desire.
We can then treat the 'typescript-helpers' as any other library, and when bundling, with SystemJS for example, it'll automatically pick out the files required for the bundle to function. The idea of managing the helpers ourselves isn't great for maintainability.
Should be addressed by https://github.com/Microsoft/TypeScript/pull/9097. a new flag --importHelpers allows for importing helpers from a standalone runtime library (still to be published on npm), and the compiler will emit imports to this library whenever a helper is needed.
This is marked with Milestone 2.1, but on the Roadmap it's under v. 2.0.
updated the roadmap. thanks.
This should be in typescript@next already. New --importHelpers results in importing emit helpers from "tslib". See https://github.com/Microsoft/TypeScript/pull/9097#issue-159760865 for more details.
Users are expected to include tslib in their production bits. i.e. as a node dependency for node packages, or as a script tag for html applications.
tslib is hosted at https://github.com/Microsoft/tslib; and available at npm install tslib. please see https://github.com/Microsoft/tslib for more information.
Most helpful comment
This should be in
typescript@nextalready. New--importHelpersresults in importing emit helpers from"tslib". See https://github.com/Microsoft/TypeScript/pull/9097#issue-159760865 for more details.Users are expected to include
tslibin their production bits. i.e. as a node dependency for node packages, or as a script tag for html applications.tslib is hosted at https://github.com/Microsoft/tslib; and available at
npm install tslib. please see https://github.com/Microsoft/tslib for more information.