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

Support compile targets between ES5 and ES6 #4389

Closed
LPGhatguy opened this issue Aug 21, 2015 · 25 comments
Closed

Support compile targets between ES5 and ES6 #4389

LPGhatguy opened this issue Aug 21, 2015 · 25 comments
Labels
Fixed A PR has been merged for this issue Suggestion An idea for TypeScript

Comments

@LPGhatguy
Copy link
Contributor

As of August, 2015, there are several platforms that have JS implementations falling somewhere between ES5 and ES6. Chrome 45+ has block scoping, fat-arrow functions, Map/WeakMap/Set/WeakSet, and classes. io.js 3.x has all of these features as well except fat-arrow functions, but they're coming in Node.js 4.0 or so next month.

The big common case here is that none of these platforms support ES6 modules yet -- I imagine none of them will for some time.

Can we get a compiler mode to emit ES6 features selectively, or at least emit ES6 except for modules? @mhegazy mentioned in the TypeScript gitter that this would be effectively supporting --t es6 alongside --m commonjs in terms of compiler flags.

@vilicvane
Copy link
Contributor

👍

Though you may try something like System.js, for now.

@LPGhatguy
Copy link
Contributor Author

Well, System.js is a partial solution -- it's just another package manager in the end. The primary reason for wanting this feature, node supporting a good deal of ES6, makes CommonJS the only reasonable target.

@kitsonk
Copy link
Contributor

kitsonk commented Aug 21, 2015

I would think decoupling packaging emitting from other features would make sense... I see situations where there is a valid use case for emitting UMD/AMD/CommonJS modules while still targeting ES6 at the code level without additional build steps. 👍

@danquirk danquirk added the Suggestion An idea for TypeScript label Aug 21, 2015
@mhegazy mhegazy added the In Discussion Not yet reached consensus label Aug 21, 2015
@lhecker
Copy link
Member

lhecker commented Aug 22, 2015

As a feature this would be single most important thing for me right now, since I could effectively use type annotations while using all the ES6 features available in io.js. 👍

@LPGhatguy
Copy link
Contributor Author

I'd like to note that this is technically possible right now with some rough edges with module resolution if you chain TS by compiling it to ES6 then running it through Babel with only the module transformer enabled. There could be other issues, and I'm not sure how TypeScript handles ES7 translation features either.

Actually, with ES7 coming up I think it's going to be more and more important to selectively enable future JS features as different platforms enable varying support. We're not going to get modules for awhile, but what about annotations from ES7? Async/await? Some of these things could be implemented by some platforms and those features should be used on clients like io.js where the requirements are known ahead of time.

@lhecker
Copy link
Member

lhecker commented Aug 23, 2015

On the other hand a mode, where you simply write the "near-ES6" syntax of your platform using the e.g. CommonJS syntax (io.js), but treating files which use this syntax just like they are ES6 modules, would be possible too (for me atleast) - all the transpiler would need to do is to basically strip out the TypeScript-specific features.

Currently, if you try to do this, it won't work since the compiler treats "local" variables automatically as global ones, if the ES6 module syntax isn't used, and when using e.g.

const foo = require("./foo");

in more than one file it will throw

error TS2451: Cannot redeclare block-scoped variable 'foo'.

@jbrantly
Copy link

@lhecker I think that just means you're not specifying --module if I'm understanding you right.

You can use --target es5 --module commonjs and write import foo = require('./foo').

@lhecker
Copy link
Member

lhecker commented Aug 23, 2015

@jbrantly Nope that's not what I meant.

What I was saying is that an alternate solution for now would be to mark all files using require() or exports as ES6 modules, thus making file-local variables actually local to the file (which would match CommonJS behaviour and not the current Browser-like one).

That way you could at least already use the latest native ES6 features of io.js (--target es6), without automatically adding shims you don't need. One could also introduce a new target value to supress accidential shims (e.g. --target native) and enforce the use of native ES6 features, but I guess the thing I mentioned in the previous paragraph is much more important.

@jbrantly
Copy link

@lhecker I think I see what you're saying. You're specifying --target es6 because you want the es6 emit, but you can't use import foo = require('./foo') in that case, so you're skirting around that by not using import but then running into the scope issue.

You could always use IIFEs 😄

I'm not sure how I feel about this issue overall. If you're targeting the browser then you're going to have to target the least common denominator anyways, which means es5 (and a bring-your-own module system). If you're targeting a server environment where you control everything, sure, you might be able to use some ES6 features that you don't want to transpile but what is the harm in transpiling anyways? Or, alternatively, emit ES6 from TypeScript and use something like Babel whose core competency is transpiling to get CommonJS (whereas TypeScript's core competency is static type checking). I think TypeScript currently does a good job balancing transpiler features and simplicity with the es5 and es6 targets.

@LPGhatguy
Copy link
Contributor Author

@jbrantly it could also be possible to target only a specific subset of browsers.

Additionally, transpilation kills the benefit of some structures, like numeric for loops with block-scoped variables:

for (let i = 0; i < 10; i++) {
    // ...
}

TypeScript also has issues with for-of loop code generation because of the compile-time ambiguity and the way iterators work. io.js doesn't have that issue, and I'd like to iterate over arbitrary iterables, which would require ES6 output.

@yortus
Copy link
Contributor

yortus commented Aug 26, 2015

This would be great.

Another pain scenario at present is trying to use ES6 generators, which are supported by both TypeScript and Node.js. However if one targets ES5 for Node.js projects, TypeScript won't allow the generators even though it could just emit them as-is (like it does for ES6) if some flag would allow.

One can wait for TypeScript to implement down-level emit for generators in ES5, but that seems unnecessary since the target platform supports generators natively.

I'm aware one can target ES6 and add Babel to the build process, however given that TypeScript can already emit generators, if feels like unnecessary complexity.

More granular targetting in TypeScript would be a great solution IMO.

@plantain-00
Copy link
Contributor

This would be great.

1.4 seems support -m commonjs -t es6 already, but 1.5 does not.

@antonio-yieldify
Copy link

Right now, the only reason I'm not using Typescript in my nodejs projects is because of the impossibility to use generators in an ES5 context.

It's a reality that node.js and io.js provide a runtime that overlaps the ES5/ES6 specs. There are many useful functionalities as consequence.

The use of Generators with Promises reduces asynchronous code complexity which greatly improves maintainability on projects that heavily rely on I/O logic which is one of the core premises when working server-side.

+1 for this kind of granularity

@yortus
Copy link
Contributor

yortus commented Sep 8, 2015

I made a proposal for a possible solution over at #4692. There is a working implementation too.

@dsebastien
Copy link

Until there's broader support for ES6 on the platforms that I target (in my case web browsers) or more down-level emit support in TS, I prefer to let TS emit ES6 and pass that through Babel to get ES5 compatible code. That way I can use more features already. Indeed at the cost of a higher build complexity (though that remains manageable so far ^^).

@timjroberts
Copy link

Node 4.0.0 supports many ES6 features now that I think would make this issue more broader. The most important feature for me is that it supports generators now, bringing async/await into the tool set of anyone wanting to use TS 1.6 from beta.

I'm also pretty sure that the 1.6 nightly builds allowed async/await with ES5 as a target until recently.

@irakliy81
Copy link

+1

@avocadowastaken
Copy link

+1, we need to debug generators

@mhegazy
Copy link
Contributor

mhegazy commented Oct 14, 2015

if you are using typescript@next you should be able to specify --target ES6 and --module commonjs and get generators working on node v4. see #4811 for more details.

@mhegazy mhegazy closed this as completed Oct 14, 2015
@mhegazy mhegazy added Fixed A PR has been merged for this issue and removed In Discussion Not yet reached consensus labels Oct 14, 2015
@leebenson
Copy link

A good example is rest parameters.

Using --target es6 generates code that Node 5 can mostly run natively (it has around 59% coverage of the full ES2015 spec so far). let, class (with strict mode), generators, fat arrow functions, etc all work fine.

But rest/spread doesn't.

+1 for selectively compiling features against different targets.

Would love to see something in tsconfig.json like:

{
    "compilerOptions": {
        "target": {
            "rest": "ES5",
            "spread": "ES5",
            "decorators": "ES5",
            "*": "ES6"
        }
    }
}

@afaayerhan
Copy link

yes @leebenson I came reading for this. selectively targeting some features would let typescript fallback to transpiling to es5. I think there would be not so much work to do more than just adding it to tsconfig and making few more changes for typescript. then we can use more ES6 features and without another build step with babel. and also having typescript sourcemaps directly while debugging nodejs code.
+1 for

{
    "compilerOptions": {
        "target": {
            "rest": "ES5",
            "spread": "ES5",
            "decorators": "ES5",
            "*": "ES6"
        }
    }
}

@pleerock
Copy link

pleerock commented Feb 2, 2016

because not all es6 features are supported I have to compile down to es5 from es6 using babel =( btw is it possible to compile from es6 to es5 using latest typescript features?

@afaayerhan
Copy link

@pleerock if you want to bypass babel an you are uisng nodejs. you can just set target es6 and enable nodejs native features with harmony flags for example to enable destructring you will pass this flag like this node --harmony_destructuring server.js

@leebenson
Copy link

@afaayerhan with Node 5.*, most of that stuff is enabled by default. You can use my babel preset for anything that's missing from the ES6 spec - then you just run with babel-node instead of node, and you're good to go.

@afaayerhan
Copy link

thanks @leebenson I have used such in react with typescript but have not tried it with node js. do typescript source maps work with debugging in nodejs when using your babel presets?

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
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