如何最好地模拟 Typescript 中的命名空间?

How to best emulate namespaces in Typescript?

我意识到命名空间在 ES6 中基本上已被弃用,并且在 Visual Code 中效果不佳。

但我还是想用它们。为什么?因为我的代码分布在多个文件中,所以我想在不创建它们之间的依赖关系的情况下对其进行分组。

这是用例。我在不同的文件中有两个不同的商店:

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

   export const fooStore = new FooStore();

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

   export const barStore = new BarStore();

出于可发现性目的,我想将这些商店组合在一起,以便开发人员可以参考 stores.fooStorestores.barStore

一种解决方案是导出包含它们的对象,如下所示:

export const stores = {
  fooStore,
  barStore
};

这是有效的,但它有一个很大的缺点。如果任何代码引用 stores.,那么所有引用的存储及其所有依赖项都会被拉入。为什么这是一个问题?因为我在一个代码库中工作,该代码库是 AngularJS 类(遗留代码)和 React/MobX 代码(我们要移植到的代码)的组合,而且我们的一些商店使用 AngularJS。当我编写单元测试或 Storybook 故事时,我不想为我什至不感兴趣的商店设置所有必要的依赖项。

我真正想做的是这样的事情,但我知道不建议这样做。那么我应该怎么做呢?

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

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

我能想到 2 种平庸的方法来实现这一点,但它们都有缺点:

1。使用延迟 getter 在访问时加载商店。

const stores = {
  get fooStore () {
    return require("path/to/fooStore");
  }
}

像这样的动态要求可能在您的构建步骤中表现不佳。

2。让商店在初始化时向商店的商店注册自己:

export const stores = {};
export class Store {
  constructor(storeName) {
    stores[storeName] = this;
  }
}

这确实需要加载 FooStore 文件才能在商店中的商店中使用。

One solution is to export an object that contains them

是的,这会导致问题,因为它需要引入依赖项,才能将商店实例放入您的对象中。相反,为此使用带有命名导出的模块:

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

然后你可以使用命名空间导入对它们进行分组:

import * as stores from 'stores';

console.log(stores.fooStore.numFoos);

这仍然允许 tree shaking - 上面的模块没有引用 stores.barStore,因此不需要加载该模块(即使它被声明为依赖项)。