nicojs
nicojs

Reputation: 2055

Declare a global variable using an external module in TypeScript

I'm authoring an npm module in TypeScript https://www.npmjs.com/package/html2commonmark. The module can be used in nodejs (by using require) and from the browser (by loading node_modules/html2commonmark/dist/client/bundle.js in your browser).

I've recently added the *.d.ts files in order to get typing information when using "moduleResolution": "node". This works great, when installing my module it is ready to be used in typescript. Thus: the following piece of typescript code is compiling without errors:

// After installing using npm install html2commonmark
// using "moduleResolution": "node"
import * as html2commonmark from 'html2commonmark';
let converter = new html2commonmark.JSDomConverter();

Beautiful!

Now i want to run my module in a browser. As mentioned before, i need to add a script tag to node_modules/html2commonmark/dist/client/bundle.js to my index.html page. After that, the html2commonmark global variable should be available. The problem is: how do i let the typescript compile know that the global variable is there? The following piece of TS code won't compile:

let converter = new html2commonmark.BrowserConverter(); 
// error TS2304: Cannot find name 'html2commonmark'.

Even if i add a global.d.ts file, I don't seem to be able to both import my external module and declare my global variable:

// Something like this does not work :(
import * as b from 'html2commonmark';
declare var html2commonmark: typeof b;

I understand why this is. By using the import keyword my ts file is converted to an external module and thus needs to be imported. Yet i feel like my scenario is a common one. Namely: an npm module containing both an npm component and the bundle for the browser which exposes the functionality as a global variable.

Is there any way to declare the global variable using the definition inside my external module? I don't feel like rewriting my api as a (DefinitelyTyped-style) namespace while i just written my entire source code in TS...

Upvotes: 2

Views: 3082

Answers (1)

C Snover
C Snover

Reputation: 18766

The correct ways to write an external module that works in both a browser and in Node.js is:

  1. Use the --module umd flag and then use an AMD loader in the browser to load the module
  2. Use the --module commonjs flag and then use a bundler like webpack or browserify

In other words, when you start writing modular code, blindly adding <script> tags to an HTML file is no longer the correct way to load that code into a browser. Just as you should not touch the global scope in Node.js, so too should you not touch the global scope in a browser. You could hack it with a if (typeof window !== 'undefined') (<any>window).global = yourObject;, but seriously, don’t ever do this, it is wrong and it will break.

As far as exposing interfaces from within external modules to a global scope, there is no way to do this, since you would have to import the external module to get a reference to its interfaces, at which point you’ve created another external module. As of TypeScript 1.8, you can augment global types from inside modules by using declare global.

Upvotes: 2

Related Questions