-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Consider allowing access to UMD globals from modules #10178
Comments
Instead of allowing access to all UMD globals regardless of context, wouldn't it be simpler to only allow access to the UMD global if the UMD module has been explicitly "referenced" (not imported) via either the |
In other words, why not just allow |
If we said Conversely if it's only available in that file, then you're going to have to be copying and pasting reference directives all over the place, which is really annoying. These directives are normally idempotent so introducing file-specific behavior is weird. |
I see. Well, if this isn't affecting anyone else, perhaps it's better to maintain the current behavior. I'll just have to transition fully towards importing things as modules. Edit: glad to see this does affect many people besides myself. |
Current theory is to just ask people to migrate to using modules or not. If other people run into this, please leave a comment with exactly which libraries you were using so we can investigate more. |
I am using lodash, which does not come with typings of its own. I also have a situation where in my runtime environment it's easiest to use relative path import statements. So, I have a combination of import statements with local relative paths and folder relatives ('./foo' as well as 'N/bar'). If I manually copy the Up till now my workaround was using I hope this is a scenario close enough to this issue? |
Can you clarify
and
please? I'm not familiar with this scenario, so what is the purpose of this and why is it helpful? |
Hi guys, I'm in the process of looking for a solution to current In short, I'd like the following to work:
Current result: Expected result: This problem is particularly difficult, because it's triggered by I'd really appreciate a way to use UMD modules as both importable modules and as globals. Thanks. ----------------------------- Update I ended up solving this issue differently (namely: by |
The Anecdotally I remember when moment removed their global Of course a UMD package will be available in the global scope at runtime, but when it becomes available depends on the order in which other modules are loaded. |
+1 on this. I was just trying to use React with SystemJS, and as React doesn't bundle very well, I'm just loading it straight from the CDN in a script tag, and thus the React/ReactDOM objects are available globally. I'm writing code as modules as best practice, but this will be bundled (Rollup) into one runtime script that executes on load. It's a pain (and a lie) to have to
However this results in the error If this just worked, this would be far simpler than pretending in the code it's a module, then configuring the loader/bundler that it's not. (Or alternatively, I kinda got it to do what I expected by adding a file containing the below. Now my modules can use React & ReactDOM as globals without error, but it's kinda ugly/hacky - though there may be a simpler way I've missed): import * as ReactObj from "react";
import * as ReactDOMObj from "react-dom";
declare global {
var React: typeof ReactObj;
var ReactDOM: typeof ReactDOMObj;
} |
I also agree with options three plus globals: [] backup. That seems pretty intuitive to new and old users and would give the exact functionality that people need. I'm not a specialist on the code so I can't really say if 2 would be more preferable or not but I think it would also be intuitive given the configuration for it is straightforward. If I wanted to look into help implementing any of these where should I go? |
This really should be behind a flag. It is a massive refactoring hazard. Even if the flag is specified true by default. I think this has to continue to work in the original scenario otherwise we're losing the primary benefit of UMD declarations. |
@billti can you elaborate?
The only reason I wrote that in the tutorial is because using |
See this issue (rollup/rollup#855) for one example on how they're trying to optimize bundling and the sizes observed. Effectively in my setup (using Rollup) I saw minimal size gains bundling React, so I'd rather just serve it from a CDN. To me that has the benefits of: a) Less requests (and bandwidth) to my site. Looking in the Chrome Dev Tools on loading the site, React and React-dom (the minified versions), on a GZipped HTTP connection, are only 47kb of network traffic, which is less than most images on a site, so I'm not worried about trying to reduce that much anyway, unless there's really big gains to be had (e.g. 50% reduction). |
As an addendum: I'd also note that without this option, you are also forcing folks to use a bundler that elides these imports, as the TypeScript compiler itself has no configuration for saying "this module is really a global", and will thus emit imports (or requires, or defines) for modules which wouldn't resolve at runtime. |
@billti SystemJS has full support for this scenario. You can swap between using a locally installed package during development and using a CDN in production. It also has full support for metadata which specifies that all imports should actually indicate references to a global variable that will be fetched once and attached to the window object when first needed, in production this can come from a CDN. I haven't done this with react but I have done it with angular 1.x |
Thanks @aluanhaddad . Interesting... I was actually trying to get something similar working that led me to this roadblock, and couldn't figure it out, so just this morning asked the question on the SystemJS repo. If you can answer how to achieve systemjs/systemjs#1510 , that'd be really helpful :-) Note: My other comment still stands, that the emit by TypeScript itself is not usable without this, as you need something like SystemJS/WebPack/Rollup etc... to map the imports to globals for the code to run. |
I'll take a look and see if I can make a working example, I haven't done it in quite a while and I don't have access to the source code that I had at the time but I'm hundred percent sure it's possible. To your second point, that's exactly what SystemJS does. It will map those imports to the global and understands that the global is actually being requested and has already been loaded. The output is definitely usable |
FYI: I got this working in SystemJS using the SystemJS API and added my solution on systemjs/systemjs#1510 . Thanks. Re my second point: Yes, I know that's exactly what the loaders can do. That's my point, they can map an imported module to a global, but TypeScript can't - so you have to use a loader to make your code valid at runtime. So it's a catch-22 with this original issue, where you can't declare that the global (in this case React) is available in the module, you have to import it as if it were a module (which it isn't). |
@billti I don't understand. What is a scenario in which your only option is to use the global version of a module, but TypeScript doesn't allow you to do that? I've only seen scenarios where a library is available as both a global and a module. |
Accepting PRs for a new flag that allows access to UMD globals from all modules. Implementation-wise, it's quite straightforward... but we do need to name it. We kicked around a dozen terrible names at the suggestion review meeting and hated all of them, so it's up to y'all to come up with something palatable. Please halp. |
What were they?
Maybe |
I would suggest @RyanCavanaugh Did the team discuss about the type issue? |
@FranklinWhale yes. |
@saschanaz Asked this already, but I'm also curious... @RyanCavanaugh Do you remember what terrible names were discussed? |
I think the chain went something like this
I would choose the last one if forced to |
Thank you! I like Using the prefix "allow" matches the naming convention of other options, which is good. Plus... there are other options that are about as long :)
|
What is the current workaround? I have been googling for the past hour and can't find any workable solutions. We're using tsc 3.2.2 with lodash statically included in the top HTML file; with require.js; d.ts obtained from latest DefinitelyTyped; example code that fails to compile:
(please don't tell me I need to turn the project upside down, I know we're behind the curve in some aspects) Update: ((window)._)/* FIXME #10178 */.sortBy(...) works but dear Lord, it is ugly :-P |
@Gilead, solution from this comment works just fine for now: #10178 (comment) |
Is there any progress on this? I have a case where the mentioned work-around doesn't seem to work (using First I tried this: import 'firebase';
declare global {
const firebase;
} This implicitly gives the global However it doesn't actually work (I assume because it then switches to using it as type So I tried the workaround mentioned here without success (it works for others though): import _firebase from 'firebase'; // same with = require('firebase')
declare global {
const firebase: typeof _firebase;
} => 'firebase' is referenced directly or indirectly in its own type annotation. [2502] I also tried import * as _firebase from 'firebase';
declare global {
const firebase: typeof _firebase;
} => Circular definition of import alias '_firebase'. [2303] And finally, if I just do the 'firebase' refers to a UMD global, but the current file is a module. Consider adding an import instead. [2686] If anyone has a solution for this, it would be very appreciated. Otherwise any of the suggestions to solve this that were mentioned so far seems fine to me really (flag, triple slash reference, Re @aluanhaddad's comment
I'm not trying to go back to globals, I'm just trying to have a couple heavy dependencies load separately from my app bundle, while still using modules for everything else, because it brings a few benefits: the dependencies can be cached properly because they don't get updated as often as my app bundle; my bundle size doesn't explode (sometimes because of code-splitting the bundler includes the same dependency multiple times), which means less downloading for my app users; rebuilding my bundles during dev is way faster. |
This is really a very easy feature to add; it'd be great for a community member to take it up. |
@RyanCavanaugh I looked into it and kind of figured out I have to add the option to |
@simonhaenisch You can declare it in a non-module declaration file, and to avoid of circular reference, you can re-export it in another UMD module declaration. The original // umd.d.ts
import firebase = require("firebase");
export import firebase = firebase;
export as namespace UMD;
// global.d.ts
declare const firebase: typeof UMD.firebase; Unfortunately, what we declared is a value, not a namespace, so you can't do something like |
Similar to the previous comment, we are lazy-loading hls.js (UMD) and reference types thus: In
In the
Tested in Typescript >= 3.0.1 and 3.4.1. Rationale is incomplete browser support for dynamic module imports. |
@MatthiasHild Can it be done without the EDIT, yes it can, as long as the declaration is within an included |
Feedback from #7125 is that some people actually do mix and match global and imported UMD libraries, which we didn't consider to be a likely scenario when implementing the feature.
Three plausible options:
Sounds like it would work but probably wouldn't:
I'm inclined toward option 3 for simplicity's sake, but could see option 2 if there's some reasonably good syntax or configuration we could use in a logical place. Detecting the use of a UMD global in a TSLint rule would be straightforward if someone wanted to do this.
One path forward would be to implement option 3 and if it turns out people make the "forgot to import" error often, add a tsconfig option
globals: []
that explicitly specifies which UMD globals are allowed.The text was updated successfully, but these errors were encountered: