typescriptecmascript-6es6-modulesjavascript-namespaces

How to best emulate namespaces in Typescript?


I realize that namespaces are essentially deprecated in ES6 and don't work well in Visual Code.

But I still want to use them. Why? Because I have code spread across multiple files that I want to group without creating dependencies between them.

Here's the use case. I have two different stores in separate files:

   // fooStore.ts
   class FooStore { numFoos = 0; }

   export const fooStore = new FooStore();

and

   // barStore.ts
   class BarStore { numBars = 0; }

   export const barStore = new BarStore();

For discoverability purposes, I would like to group these stores together so a developer could refer to stores.fooStore and stores.barStore.

One solution is to export an object that contains them both, like this:

export const stores = {
  fooStore,
  barStore
};

This works, but it has a big downside. If any code refers to stores. then all referenced stores and all of their dependencies are pulled in. Why is this a problem? Because I am working in a codebase that is a combination of both AngularJS classes (legacy code) and React/MobX code (what we're porting to), and some of our stores use AngularJS. When I write a unit test or Storybook story, I don't want to have to set up all the necessary dependencies for stores that I'm not even interested in.

What I would really like to do is something like this, but I understand it's not advised. So what should I do instead?

 // fooStore.ts
 class FooStore { numFoos = 0; }

 namespace Stores {
   export const fooStore = new FooStore();
}

Solution

  • One solution is to export an object that contains them

    Yes, that'll cause problems since it requires the dependencies to be pulled in, to put the store instance in your object. Instead, use a module with named exports for this:

    // stores/index.ts
    export { fooStore } from './fooStore.ts';
    export { barStore } from './barStore.ts';
    

    Then you can use a namespace import to group them:

    import * as stores from 'stores';
    
    console.log(stores.fooStore.numFoos);
    

    This will still permit tree shaking - the above module doesn't refer to stores.barStore, so that module doesn't need to be loaded (even if it is declared as a dependency).