Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iterators with ES5 target #4031

Closed
mvolkmann opened this issue Jul 26, 2015 · 17 comments
Closed

iterators with ES5 target #4031

mvolkmann opened this issue Jul 26, 2015 · 17 comments
Labels
Fixed A PR has been merged for this issue Suggestion An idea for TypeScript

Comments

@mvolkmann
Copy link

This is related to #3164 which implies that TypeScript will NEVER support using generic iterators when the target is ES5. I understand that supporting this requires design tradeoffs that the team wants to avoid. However, if this is not done, it's difficult to see how TypeScript can be considered a serious alternative to Babel and Traceur for ES6 support. It will be a long time (2 years?) until most web apps will have as their minimum browser requirement only browsers that support ES6 iterators. I really think the direction presented in issue 3164 needs to be reconsidered.

@s-panferov
Copy link

can be considered a serious alternative

@mvolkmann just want to ask why they must be alternatives? You easily create TS(es6) -> Babel -> es5 pipeline with such tools as webpack.

@mvolkmann
Copy link
Author

Yeah, I considered that option and even tried it to verify that it works. However, I don't think that's an acceptable solution for development on medium to large projects. I would increase build times too much.

I think TypeScript should aim to be a complete solution for transpiling ES6 to ES5.

@wgebczyk
Copy link

true, complete transpiler instead of multiple packages for packages for packages...

@danquirk danquirk added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Jul 27, 2015
@wgebczyk
Copy link

I understand that some might want not to generate something for ES5.

Maybe we could introduce "conversion levels/tiers".
Tier1: everything is converted to make it work on designated ESx
Tier2: if feature from this level or Tier3 is used, then generate code for that, otherwise error+quit
Tier3: if feature from this level is used, then generate code for that, otherwise error+quit
TierN: if target ESx does not support used feature, raise error and stop code generation

or have some enumerated switch "--features:iterator;arrow;bow;star;moon;etc" that defines what features should be polyfiled.

What do you think?

@kitsonk
Copy link
Contributor

kitsonk commented Jul 28, 2015

Personally, I don't want TypeScript solving all this stuff for me. Especially polyfilling can have huge negative consequences when you are running other scripts in an environment. Anyone who has complete control of the code running in the their runtime environment on their page hasn't either worked on a big enough project or is part of a small company organisation. I have actually seen instances where you have had to feature detect a native implementation or not because the polyfill isn't "quite" the same as the real thing.

Some stuff is impossible to fully polyfill too, for example subclassing Arrays in ES5 is impossible. WeakMap was impossible until .clear() was removed from the spec. TypeArrays, forget it!

I would rather encapsulate functionality and offload it to native implementations if present, with the possibility in my toolchain.

What would benefit though, instead of TypeScript solving everything, is that there was a coherent "plug-in" architecture which allowed me easier access at compile time, to the AST, which I could provide code which handles the emit. That way, if I chose to polyfill some of functional ES6 code, versus the language constructs, I could.

TypeScript polyfilling all the ES6/ES7 functionality will end in tears for TypeScript and I am glad the team have resisted it.

@wgebczyk
Copy link

From my point of view, hen you write TS:

func(aaa => aaa.toString());

and when targeting ES5 you get something like:

func(function(aaa) { return aaa.toString(); });

So right now TS generates code for non existing functionality. Why not generate that for other features?

I respect that from perspective of person that has years of JS experience and tooling and chaining and knowing all tricks and browser inconsistencies, generating selectively might sense.

There are other people that might expect that TS will hide those inconsistencies and focus on code of higher level than pure JS.

I don't want to jump into "solution" that requires packages for packages for package managers for packages. I've stumbled into "framework" Aurelia", where Hello World on HDD took about 160MB!!! 2-3 package managers and tons of packages to compile that... INSANE!

Part of a lot of issues is effect of lack of clear TS team vision, what and how features will be done and considered to be implemented or not. Some features are added because there is buzz around them (like type/import/var and none works as advertised).

@danquirk
Copy link
Member

Part of a lot of issues is effect of lack of clear TS team vision, what and how features will be done and considered to be implemented or not. Some features are added because there is buzz around them (like type/import/var and none works as advertised).

@wgebczyk could you be more specific about what you're finding lacking here? We've tried to be fairly transparent with our plans in a formal way at https://github.com/Microsoft/TypeScript/wiki/Roadmap beyond just manually tracking what's going on on the repo here. Likewise we've tried to enumerate some of our design philosophy here https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals.

As far as type/import/var, maybe a separate thread/issue would be useful if you're finding they're not meeting your needs.

@kitsonk
Copy link
Contributor

kitsonk commented Jul 28, 2015

@wgebczyk actually, lambdas are more complicated then that... When accessing the canonical this within the function, TypeScript has to emit something else to give access to the canonical this:

(() => {
    this.foo = 'bar'
})();

Gets emitted as:

var _this = this;
(function () {
    _this.foo = 'bar';
})();

And lots of ES6 and ES7 are far more complicated than that. It is a big rabbit hole...

Pollyfilling all of ES6 isn't what TypeScript is about. Making JavaScript/ES more "safe" by strong typing and published guarded interfaces is what I like about TypeScript and what I think it is good at. You can write functional polyfills/shims on top of TypeScript quite easily, for example Promises and WeakMaps.

@mvolkmann
Copy link
Author

Okay, just to be clear it sounds like what is meant by saying that TypeScript is a superset of ES6 is that it will support any ES6 features that the runtime environment (browser, Node.js, ...) supports.

Using that logic maybe any current transpiling it does (ex. arrow functions) should be removed.

That's not what I want. I don't want to have to rely on another tool for transpiling to ES5.

@jonaskello
Copy link

+1. I can see from the argument from a theoretical standpoint that TS should not have polyfills. But from a practical standpoint I think most users just want idiomatic ES6 constructs to work out-of-the box with TS and not resort to double compiling with Babel or some other transpiler. (And I'm counting iterating a Map or Set with for-of loops as idiomatic in ES6).

Why not have a mode that compiles to ES5 but requires the user to provide his own polyfill for any ES6 API that the TS compiler does not handle? AFAIK Babel does it this way and it works very well.

@saschanaz
Copy link
Contributor

I hope we get some way to know a type has an iterator or not. From #4947:

Introduce a ES5-compatible way to declare iterable interfaces (Similar to what discussed in #2862)

interface NodeList iterable<Node> {}

iterable in ES6 mode will work as a shorthand of [Symbol.iterator](): IterableIterator<T> but in ES5 mode will just make an internal flag to be allowed in for-of loop.

// NodeList is marked as iterable,
// it has .length property and index signature for ES5 emit, so all good
for (let node of document.querySelectorAll("...")) {
}

This will make it work both on ES5 and ES6 without changing emit.

@amir-arad
Copy link

a big +1 on @jonaskello 's suggestion.
now I'm off to convert my beautiful useless Iterable API into something that eagerly creates long arrays on every invocation, so that it can be used in for ... of loops.
😞

@alexanderharm
Copy link

I totally second @mvolkmann considering that libraries like Angular2 and Immutable.js embrace the for ... of loop construct. I would expect that setting "target": "es5" actually results in ES5 and not having to verify that (arrow functions: check, for ... of loops: duh, ...). Right now I even have to find out the hard way since documentation is missing (https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#5.7)

@BowserKingKoopa
Copy link

I just smacked into this issue. I'm unable to get TypeScript to iterate over an Iterator with for...of. I get "x is not an array type or a string type."

@fluffywaffles
Copy link

fluffywaffles commented Jul 1, 2016

Would it be possible to enable the transpilation of for..of when targeting ES5 in tsconfig.json?

Something like, "enableForOfTranspilation": true - with the caveat that it will be slower, and requiring that you must have imported typings and a polyfill for Symbol in order to compile. I believe checking for typings for Symbol, and whether it is imported in your source either globally or as a module, is already done by tsc - but it would still require some work to perform the actual transpilation.

I am using TypeScript with Core.js, and so the Symbol polyfill is already a part of my project. This is clear from my tsconfig, seeing as I include core.js typings in order for my code to type check.

It appears to me that @mhegazy's list of reasons in #3164 are largely centered on possible performance implications. It is noteworthy Angular 2's ngFor does not allow for..of loops over Maps, Objects, etc., so perhaps this is actually an onerous undertaking or they would already have asked for it in TS / done it themselves - but if it can be reasonably implemented, I'd like to have the option.

Additionally, creating a "pipeline" whereby you transpile TS -> ES6 -> ES5 with Babel as a middleman is undesirable for obvious reasons, not least of which being the much higher compilation time incurred by compiling twice.

@fluffywaffles
Copy link

fluffywaffles commented Jul 1, 2016

Also of note, the following compiles and works (possibly only with polyfills, but still):

for (let [ key, value ] of Array.from(myMap.entries())) { ... }

This is an acceptable workaround for me.

@mhegazy
Copy link
Contributor

mhegazy commented Feb 17, 2017

Should be addressed by #12346, with --downlevelIteration.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Fixed A PR has been merged for this issue Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests