-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Suggestion: target specific runtimes instead of JS versions #19183
Comments
So you're asking for something like babel-preset-env. I don't really know how many people actually have targets that end up being drastically different from ES5 and ES2015 to be honest, so I'd be open to hearing more feedback. |
I'd like to see this in as well, as browsers are adding more features all the time, if we are able to specify the browsers that we are targeting then it will only need to compile what is required. Would suggest using https://github.com/ai/browserslist as it's pretty much the standard for this, and used by lots of other projects. Many people are only targeting the last few versions of browsers, or if you are doing a project specifically for Electron then you only really need to target a specific "browser" Would love to see this happen. |
Here goes. BackgroundAs a TypeScrpt user, I do not write code that runs on a standards-compliant JavaScript engine. I do write code that runs on one-or-more custom JavaScript engines. I would like TypeScript to warn me when I make use of an API that does not exist in the target engine at compile time, rather than debug the issue when a problem occurs at runtime. Today, TypeScript's core
While there is movement to replace these specs with specs generated from the standards themselves (see [1], [2], [3]), this still leaves us with a type system that will suggest/allow APIs that would cause runtime errors when run in certain targeted engines. The following image (taken from this comment by @kitsonk) illustrates the issue well: The numbers reported by that tool when "Specifications" is replaced with "Mozilla Firefox" (as of 2018/09/25):
As a TypeScript user, it is those 5697 APIs that I want to use. If I venture outside of the bounds of that set of APIs, at the very least I want TypeScript to warn me. That said, it would be even better if TypeScript could support me in that endeavor. An extra challenge not addressed by the simplicity of that image is that the shape of the intersection in question changes between browser (JavaScript engine) releases. Some releases will add new APIs; some releases will remove APIs. Browsers are a moving target and that is considered as part of this proposal. ProposalProvide a new
When the UsageWhen the ExampleSpecifying the following in a "target-engines": [
"dom.ie.8+",
"dom.firefox.42+",
"dom.chrome.45+",
] would restrict the types available in TypeScript's type system to those common across the ranges of the browsers specified. Engine Guards and Differentiating EnginesSimilar to TypeScript's Type Guards, Engine Guards would allow the developer to create scopes within which TypeScript's type system will adjust the available types based on the specified guard. Typically, implementing an engine guard would result in the type system expanding the set of available APIs within the guarded scope. If a developer wishes to use The
|
I would definitely like to see this feature in TypeScript. It's incredibly useful in Babel. |
Love this idea! The website I work on compiles to ES5, but practically, we are not shipping a website for ES5 compliant browsers; rather we ship a website that needs to work in a whole set of browsers. Which for me is currently last 2 versions of Edge, Safari, Firefox, Chrome, and IE 11, and a few mobile browsers too. An interesting anecdote here: we recently dropped IE 10 and noticed that our supported browsers now all have support for ES6 Set and Map... but not without some quirks in IE11, so we've forked the standard declarations to make a version that bans the stuff that won't work in IE11, and include our custom declaration file for our typechecking. So we are sort of already hacking our own version of this. As far as implementation... I think @ericdrobinson gave an excellent in depth proposal. That said, I also think it looks like a lot to implement for a first version. My suggestions:
|
@ericdrobinson good proposal. thanks. But "usage" part have a bad idea. JS world already have tool for this problem. Browserlist. We don't need reinvent existing tool. Next suggestion is compile multiple targets. It is great idea because IE11 has far less APIs. Binary size for IE11 and Chrome/FF/Edge/Safari can vary 2 or more times. Modern browser users can download binary much faster. |
|
Ah that makes some sense. That said I would love to use browser / server targets even without ts-engine-guard; it feels like a nice add-on rather than a required part of this proposed feature. Perhaps in part because my use case is mostly pure browsers and it's very rare for us to want to write code that won't work universally; I could see it coming more in handy if we have stuff that's more like |
Yes, that is true as written. It would be a "more complete" implementation with But, yes, this would be a secondary feature implementation - to be implemented once the core mechanisms of specifying a set of target runtime environments were in place. :) |
i'd support this, specifically for targeting non-browser environments like Duktape, Espruino, Adobe ExtendScript, etc. Or "weird" browsers like embedded devices stuck with old Android versions (e.g. car multimedia). An obvious problem is a combinatorial explosion of cases to test if we try to support "downlevel to run on Duktape + nw.js". Possible long-term benefit is that breaking down engines into feature sets may enable easier maintenance for new features, though. |
@wizzard0 those using esoteric runtimes can always |
@kitsonk I'll point out that this proposal isn't about asking TypeScript to keep up with esoteric runtimes. As you point out, you can already pass the The idea is that each runtime provider [the esoteric included], could provide their own declaration files. This wouldn't be anything special and would not warrant a new feature proposal by itself. The difference is that the proposal outlines a suggestion to take a union of several different type declaration libraries based on setup. Imagine that you're a web developer working on an Open Source library. You want to make sure (or at least have some assurance) that the code you write will work in a specific set of browsers and their versions. What's more, you want to ensure that the code (or some portion thereof) can be run in NodeJS. Today's TypeScript may help quite a bit but you can still end up using APIs that simply aren't supported in one runtime or another. Wouldn't it be nice if you could tell TypeScript which runtimes you wanted to target and it would simply adjust the types it resolves (and, therefore, the errors it throws) as a result? Support for esoteric engines would come for free, provided the engine developers provided whatever necessary [versioned] libraries would be required to support such a system. As for downlevel compilation, I think that should be raised as a separate issue, especially as TypeScript language services can be leveraged directly in pure JavaScript files as well. |
ideally:
In which case the core problem becomes adding a type system feature to combine the different environments' type declarations. re downlevel compilation, this task does seem to have talked mostly about the type system implications of multiple environment support; so I think I agree with @ericdrobinson that whether to support any changes to the typescript output should probably be a separate issue; so far, I've found using es5 output to be suitable for my use cases. Perhaps it's time to start proposing the necessary type system changes? |
For my own purposes, I'd be fine w/ exactly what babel-preset-env does. In the babel community it's effectively an anti-pattern at this point to target anything other than whatever environment you intend to support (i.e. to target specs). In many of the responses to this and related issues (#20095, #23156 (comment)) I hear the notion that it's not reasonable to expect TS to keep up what browsers support, and I totally agree. Which made me think, how does babel able to keep up with the evolving browser landscape? Turns out they dont. So, I wonder, is it possible to TS to implement or to extend TS w/ something that works just like that? |
I think there are two threads to this conversation: These actually seem to be disjoint approaches to the problem of "how do we handle nonstandard parts of our environment" - e.g. do you polyfill missing features (option a), or do you disallow their usage (option b)? I'm arguing for (b), and I believe @ericdrobinson is on the same page. @tomwayson you seem to want (a). Which is fine, but it's a different use case - e.g. I don't want random polyfills showing up in my compiled code, as they tend to add unexpected bloat and some do sketchy things to builtins (e.g. the closure compiler's WeakMap overrides Object.freeze & Object.seal); I want to use types to restrict ourselves to only what's implemented natively, plus what we've explicitly decided to polyfill. In particular, what I'm hoping for is infrastructure to be able to plug in type declarations from arbitrary browsers - so I can get typings that are |
That is correct.
That is precisely the idea. The main proposal covers the why (the problem) and the what (a mechanism by which TypeScript may be engineered to help solve the problem). When it comes to sourcing the data that is actually used by the "mechanism" (the "Engine Library Files" [ELF]), I did not specify that the TypeScript team should take on the impossible task of providing/sourcing this data themselves. To my mind, the strongest solution would be the last one presented in the proposal: "Request engine vendor buy-in and support." This would mean that engine developers are responsible for delivering type declarations for their products. See:
[Note: There is nothing magical about the term "Engine Library Files". That is simply a term I used to keep the conversation focused. The term is 100% equivalent to "Type Declarations for specific JS engines".] There is actually nothing stopping those vendors from doing so today: they could generate engine-and-release-specific Type Declaration files and TypeScript would happily consume them. What is not currently possible is the ability to specify multiple target engines (Type Declaration files/ELFs) and select only those APIs that are available across those specified engines. Which... is what the proposal is all about :) |
@ericdrobinson re:
That's no longer true. As per "browsers" Browserslist can also target Electron and Node versions which I suspect might cover most of the "other" targets a TypeScript project may have. |
@wrumsby That's neat! While not a comprehensive list of engines, it's certainly a strong one. There's still the other issues I brought up in that section, though. :/ |
One thing I have done is to run ESLint with eslint-plugin-compat over the JavaScript code generated by TypeScript. With this plugin you can specify polyfills so a workflow can be:
(Ideally you would describe the polyfills in a way that both eslint-plugin-compat can use them and they're automatically added to the page using https://polyfill.io) |
TSC support for browserslist would be very good to see. At the very least, using browserslist can reduce the number of polyfills that have to be injected into distributable files. If we only need to target the last 2 Chrome, Edge, and Firefox versions, the number of required polyfills would be dramatically reduced, and would result in smaller (and potentially more performant) runtime code. Adding a path to a standard browserslist file, to the TSC config, should be sufficient. |
This is indeed a very nice feature to have. By the way, guys, do you know if there is existing compiled data that we can use to at least manually map different browser versions to TypeScript targets? |
Off the top of my head I don't know of any that lists browser -> js spec
version. But https://github.com/mdn/browser-compat-data will give you
browser -> which js features are supported. This data is also used to
power caniuse.com
There's also https://kangax.github.io/compat-table/es6/
<https://kangax.github.io/compat-table/es5/> which might be closer to what
you want. Haven't used it before but it seems likely to be accurate
…On Fri, Nov 29, 2019, 8:59 PM Slava Fomin II ***@***.***> wrote:
This is indeed a very nice feature to have.
By the way, guys, do you know if there is existing compiled data that we
can use to at least manually map different browser versions to TypeScript
targets?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#19183?email_source=notifications&email_token=AAIGO52JFWZGGHQQZTIKLQDQWHCINA5CNFSM4D7FYYQKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEFPXLHI#issuecomment-559904157>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAIGO5YMRQIAXRCO7JQTQULQWHCINANCNFSM4D7FYYQA>
.
|
@dgoldstein0 Babel has this feature through the use of the preset-env. (Which likely uses that behind the scenes). |
I came here looking not for automatic pollyfilling or anything like that, I just wanted to have a warning when my |
@thw0rted While this seems related, I would highly suggest creating a separate Feature Request for this (and perhaps referencing this proposal). Another option would be to suggest this in a comment on #16607 as a "Browserlist-aware API Usage Checker" sounds like a great candidate for a compiler plugin. |
Yeah that is a bit different than what we're suggesting here: we're saying
instead of target: es5 or target: es2015 or similar, why not target:
ie11,chrome 70+,Firefox 60+, or something similar. (The particular proposal
is target-engines)
I think they solve similar use cases, though it seems better for typescript
to just do the right thing when possible, rather than warning about doing
the wrong thing.
…On Wed, Dec 4, 2019, 10:23 AM Eric Robinson ***@***.***> wrote:
I just wanted to have a warning when my target compiler option is set too
high and would cause failure in one or more targets from my browserslist.
@thw0rted <https://github.com/thw0rted> While this seems *related*, I
would highly suggest creating a separate *Feature Request* for this (and
perhaps referencing this proposal). Another option would be to suggest this
in a comment on #16607
<#16607> as a
"Browserlist-aware API Usage Checker" sounds like a great candidate for a
compiler plugin.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#19183?email_source=notifications&email_token=AAIGO56OW5CYOGJMAOBW6F3QW7YTXA5CNFSM4D7FYYQKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEF6AMHY#issuecomment-561776159>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAIGO57BYYNSO2B3MFLGW6TQW7YTXANCNFSM4D7FYYQA>
.
|
I think I was a bit confused, reading through the history of this issue, because at times the thread conflates The way Typescript behaves today places the burden on the developer to either learn a great deal about how each browser does or doesn't support various features of various ES versions, or to use a post-processor like As a solution, I would like to slightly modify @ericdrobinson's proposal. Instead of trying to maintain a bunch of platform-specific definition files (already proposed in #23156 and rejected), we could throw in all the APIs we might conceivably use, then have For each API declared in I came to this solution because David is right -- why warn when you can just fix it? I believe all the pieces are already in place to do this. As Eric says, this might be better implemented as a plugin, but I do think it would improve the onboarding experience for new Typescript devs if they could declare target platforms in their config instead of worrying about |
@thw0rted This is unsurprising: most people conflate the ECMAScript-level APIs with Web-platform APIs when they refer to "JavaScript". This is understandable, too, as the interpreter (the "engine") does not distinguish between the two, either - they are simply "built in".
Beautifully stated! 🍻
Two things:
This is effectively what the main proposal is all about.
Such an approach would not address the issue raised here. To be more specific,
How does your solution differ from simply using the Browserslist/CanIUse combo at some stage in your development pipeline? It seems as though Your suggestion is pretty powerful - it's just very specific to the web platform (a specific [admittedly major] use case). In the main proposal, the
Perhaps it would make sense, then, to suggest this as a "plugin candidate" on #16607? That might seriously help by providing a solid use case; one that clearly states the requirements and expectations. |
I saw your earlier comments about the limited scope of Your proposal mentions Web IDL as a standardized way of expressing API surface and #3027 already links to a script to turn IDL files into a I'd still prefer to see one source of truth with one syntax used for declaring target platforms, though. |
Once again, the proposal never suggests that the TypeScript team should be responsible for maintaining the "Engine Library Files". As this follow-up comment makes more explicit, the people actually building the engines (core JavaScript interpreters, browser "layers", etc.) should produce and maintain their own ELFs. The original "get engine vendor buy-in for generating ELFs" option reads as follows in its entirety:
That second sentence is pretty crucial and shouldn't be that difficult: Microsoft already has some tooling for generating Type Declaration Files (TDF) from WebIDL specs. Browser/interpreter vendors could use these tools on WebIDL that they output during their respective build processes, meaning that generating a new ELF would be automatic. Some environments already have their own specs available. Adobe's ExtendScript-capable host applications (mostly) provide "Scripting Dictionaries" (XML files with type information that powers their own "documentation viewer"). The community has produced a tool to produce TDFs from those source files. Apple provides "sdef" ("scripting definition") files to power its documentation viewer for JXA. Once again, the community has produced a tool to produce TDFs from those source files. The question of "where does the data come from" is not one for the TypeScript team to answer - it is one for the community and, mainly, the Engine vendors themselves to handle - which they should be doing already anyway. To quote the previously referenced follow-up comment:
I completely agree that this would be useful outside the TypeScript community. That said, I think the engine vendors themselves should get their collective acts together and make exporting some form of "supported APIs" documentation/listing (WebIDL format would be peachy) a basic requirement. You could argue that they haven't "needed" to do this in the past, but I do think it would go a long way to powering enhancements with pure JavaScript tools and especially the growing TypeScript ecosystem. The community could maintain these but it makes a heck-of-a-lot more sense to have the vendors simply handle it natively. We have a bit of a chicken-and-egg problem at the moment: there's little direct benefit for engine vendors to put in the work to generate such data natively without something to consume them. And without something to consume, it makes little sense to invest a lot of time in a system (as proposed here) that can merge disparately sourced data.
Agreed!
I'm not sure I follow. Can you clarify? What "existing implementation" are you referring to? The proposal? The |
Yes, |
Got it! Thanks for clarifying. Some thoughts:
I'm not sure that it makes sense to use the I should point out that the main proposal also suggests looking towards
I have to disagree with this one. TypeScript does not care about your |
My concerns are mainly Node.js specific and a bit more targeted against lib: Node.js>=12.10 has the full support of ES2019 and already supports some bits of ES2020. However, there is no option to only enable It would help to allow more granular configuration of what APIs are available and which are not. However, the best solution would be a configuration flag where you specify your Node.js (or Browser) version and the TypeScript compiler automatically picks the APIs available for that Browser/Node.js. Example with Node.js<12.10: Should cause a compiler error |
@n1ru4l I think part of what you want can be done with the that said it's totally valid that targetting specific node versions would be useful. |
@dgoldstein0 Yes, I also was a bit confused that there was no |
according to https://www.typescriptlang.org/docs/handbook/compiler-options.html there's no es2019 or es2020 options for lib yet either. maybe these docs are stale? or maybe not. I think those deserve a separate issue - as adding es2019, es2020, and es2020.promise and similar should be immediately actionable with no extra designing. |
I believe all those identifiers ( ETA: I found this because you can select any method call, like one to |
@thw0rted Yes you are right |
So 18 months after posting my previous comment here, I'm now happy pointing tsconfig.json at ES2020 and letting Babel/Webpack handle the compiling for browsers/workers/node. TSC is now only used with VSCode and doesn't actually produce the output Workspaces in VSCode help to separate the various runtime environments for TS libs, e.g. DOM, WebWorker, so the correct type definitions are made available It could still be improved a bit but overall it does the job quite well |
@simon-robertson making sure I understand: you are using target=ES2020? what about lib? and presumably using babel (as a webpack plugin) with bazel-preset-env insert any polyfills you need? am I missing any major details? |
That is correct As an example, consider the following project directory structure ...
The base
The
The
Notice how the You define the workspace folders in a
In VSCode, you would open a workspace instead of a folder Webpack along with the Babel plugin are then used to build each of the main modules, so in this example that would be Using browserslist gives you a lot of control over the browsers/platforms you want to target. Babel will automatically use a In a nutshell, TSC does not do any compiling; in my opinion Webpack and Babel can do a much better job Obviously doing things this way requires more work to setup a project but it is worth doing, and we now have the option of creating/using GitHub template repositories for base project setups if needed |
Care to share your setup? I'm very interested! 👍 |
@codepunkt |
First advantage is target to browser is native for user than target to js version. Don't need to know what each browser support.
Second is ts can transpilate more effective. Some browser can have partially support of next standard. Ts can transpilate unsupported features only.
The text was updated successfully, but these errors were encountered: