Search code examples
typescriptes6-modules

How do you expose class in a module to the global namespace in TypeScript?


The Problem

Given

  • I have an existing application containing both modules and global scripts (regular non-module scripts).
    • modules
      • App.ts
    • global scripts
      • alpha.ts contains a class named alpha
      • beta.ts contains a class named beta
      • gamma.ts contains a class named gamma
  • I want to convert one class in a global script (gamma.ts) into a module
    • via adding export default before class gamma {} in gamma.ts
  • I want to import class gamma into the global namespace (i.e the window object) so it can be accessed by the existing global and module scripts.

What I have tried

I have tried a bunch of things around global.d.ts

e.g.

import gammaAlias from "../scripts/gamma";
export as namespace NS_Global;

declare global {
    var qqq: {n:string};
    namespace ZZZ {
       export var gamma: gammaAlias;
    }
    var gamma: gammaAlias;
}

But none has worked

i.e. TypeScript tells me one of the following

  • gamma does not exist
  • Cannot use 'new' with an expression whose type lacks a call or construct signature.

github

I have a github repo set up to investigate these questions. https://github.com/penguin020/expose-typescript-modules-globally

ConvertToModulesPOC_original is the working "before" case.

ConvertToModulesPOC is a broken attempt to convert gamma .ts to a module and expose it globally.

PS

The question remains, how do you expose a module to the global namespace?

Any answers using the examples in the github would be particularly appreciated!


Solution

  • I found an answer here in "TypeScript: exposing module types in the global context and why to avoid it";

    I have updated the github repo.

    The most salient changes I found were:


    in tsconfig:

    "module": "system",
    

    in yMod.ts

    import gammaAlias from "../scripts/gamma.js";
    
    declare global {
        export type gamma = gammaAlias;
        export const gamma: typeof gammaAlias;
    }
    
    (window as any).gamma = gammaAlias;
    

    note: You could have the global ambient declaration in another file, even a *.d.ts file.


    The link above breaks down the reasons for having three references for each symbol to be exposed globally. It is pretty well explained on that page, so I will only summarize here:

    1. Expose the class type to the TypeScript compiler (no code generated)
    2. Expose the class object to the TypeScript compiler (so there is an object to call new on; no code generated)
    3. Extend the window object to expose the class in the global namespace (generates code).