Using umd globals with modules in a Typescript project
Asked Answered
W

2

9

I am trying to get the Leaflet library to play well with my Typescript project.

My project follows the commonJs pattern (imports\exports in my source files), but when I build with webpack, the compiler gives me the following errors from my files where I try to access an L global exposed by the leaflet module

'L' refers to a UMD global, but the current file is a module. Consider adding an import instead.

Now I can get around this if I import in leaflet into my source files as follows:

import * as L from 'leaflet'

But I was wondering if I could access t globally across all my files without having to have imports in each of my files.

I had a dig around and found some workarounds where you can declare your own globals like so:

globals.d.ts

import * as L from "leaflet";

declare global {
  const L: L
}
export {};

I'm not sure if this is recommended, it feels a bit of a dirty hack, is there a proper way with webpack config to provide globals exposed by UMD modules for a commonJs project, or am I talking nonsense? I admit I get confused trying to understand the differences between the module types and their interchangeability, so go easy on me!

Woermer answered 24/2, 2018 at 8:40 Comment(0)
D
0

In module you can import another modules or use globals. No way to import module to all your modules at once. If you are lazy to write import everywhere, then you should use global variable.

UMD module system is universal. If it is imported to some module (CommonJS, AMD...), then it behaves like module, i.e. it does not populate global scope. If you want UMD to become global, you should just add it as <script> to the page. Then you can use .d.ts. file to notify TypeScript, that leafet available in global scope. There is no any hack there (besides, using global scope instead of modules is a hack by itself nowaday).

Donor answered 25/2, 2018 at 6:47 Comment(4)
It's not a matter of laziness. In my company's project we bundle *.min.js UMD library files manually via a Gulp task, but we have changed all of our TypeScript code to modules. We compile to SystemJS modules and do not have Webpack incorporated in the project and it would take significant effort to rewrite the project structure. Thus, declaring in the global namespace is necessary to be able to use UMD globals without TypeScript complaining.Dibucaine
"it would take significant effort to rewrite the project structure" - what if not lazyness and legacy? : )Donor
I'm one person maintaining/updating a 40KLOC+ codebase. While I do frequently rewrite old crappy parts of the code and enjoy doing it, I have to prioritize new features, and rewriting the module structure is on the bottom of the list. I must say I now wish I never read a blog post implying Webpack is no longer necessary because I ended up migrating the code to SystemJS modules at one point and throwing out Webpack. So now I guess I would need to undo that because TypeScript won't let me nicely use UMDs from modules (for no valid reason, as far as I can tell). >:(Dibucaine
You can do a hack like window.globalVar = yourImportedVar; or smth. There are a lot of ways to solve such legacy problems - it depends on your environment.Donor
K
0

I faced the same problem as Sam Claus in the comments. The error prevents me from easily referring to namespaces in Typescript which are provided by scripts that are loaded in script tags inside the HTML file.

My task was to make a simple static HTML report from some data using Plotly. I don't (want to) use a bundler because I didn't find it necessary at first. At the moment I use modules as individual files that are compiled from TS to JS and I load the entry module in a tag.

Plotly is imported in the classic way, the minified non-module version is loaded from a script tag in the HTML head, that I downloaded and stored locally. The static page can be used offline. Only for the type definitions, I use NPM. I avoid adding additional dependencies to the lock file by only using a locally stored minified version that I am not going to change. It is probably the most lightweight and fast technique that one could think of. I added "plotly.js" to the types options in the .tsconfig so that the namespace is available globally without import. As long as I compiled TS as a concatenation of classic scripts, I could make use of the Plotly namespace. The problem was, the concatenation order of the script pieces is not intelligently derived from file contents (besides new CORS issues during testing!). The order is fragile and not maintainable so that I turned the files into modules where import and export statements create an automatic order.

I found, there are two ways to bypass the error with UMD globals.

  1. Using NPM to get the module version of the library and refer to the module via an import map in the HTML file. See question about importing Plotly with NPM for the typescript part. Will waste memory for the package manager. In case of very few dependencies, it might not matter because a new empty file itself might be bigger than the change of the package-lock.json.

  2. Defining an alias variable.

    // @ts-ignore
    const plotly = Plotly
    

    The Advantage of the alias is that its usage does not generate errors. The usage of the "UMD global" is concentrated in only one place and therefore is easy to ignore. The disadvantage is the unnecessary variable which uses a slightly different name than the common namespace name (probably violating naming conventions).

    I only needed the import in one module and therefore put the above two lines under my import statements. But in general, all UMD globals can also be defined in and exported from an extra file using the // @ts-nocheck once at the beginning of the file to suppress all errors.

    Then, they can be imported on demand in other modules: import { plotly as Plotly } from "./dependencies.js"

    In theory, one could also default-export each from their own file. A new file plotly.js is created in the source directory:

    // @ts-ignore
    export default const plotly = Plotly
    

    which is imported as

    import * as Plotly from "./plotly.js"  // relative path to our own file
    

    But the increased number of files probably causes a higher waste in memory than just adding these dependencies with NPM.

It's a hack after all and it would be better if the TypeScript developers re-think the issue and turn the error into some kind of warning, particularly because it is not related to faulty behaviour and therefore should not be treated as an error.

Kinsey answered 3/9, 2023 at 1:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.