2

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...

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for your response! To produce the bundle.js i'm using webpack. Of course, i could also force (typescript) consumers writing for the browser to use webpack/browserify. I was just wondering if there was a better way. I feel like it should be possible. using (<any>window).global = yourObject is not needed, because the object already is available on the global scope (at least: when including the bundje.js on your page). I'm just looking for a way to make it visible to typescript.
In that case, the answer is a much simpler “no” :) You can expose global interfaces to named modules in hand-written d.ts files by declare module "foo" { export = SomeGlobal; }, but there is no way to go in the other direction.
That is too bad. If you update your answer with the short version i'll mark it as the correct answer
You are my hero: if (typeof window !== 'undefined') (<any>window).global = yourObject; ;-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.