在 TypeScript 中创建包含所有导出的模块

Jac*_*cks 5 module node.js typescript

我想达到什么目的

我正在尝试在 TypeScript 中创建一个模块,该模块将包含所需类的每个导出。事实上,真正困扰我的是import在我的类中拥有所有相对的语句,这并不是进一步维护的最佳方式。我只想使用绝对路径。

一旦被转译,具有绝对路径的类(在生成的 JavaScript 中)有错误的路径(因为它们没有被转换为相对路径,当与tsconfig.json>outDir选项结合使用时甚至更糟)。

示例用法

让我们以下面的项目层次结构为例

root
|- helpers
   |- MyHelper.ts
   |- AnotherHelper.ts
|- calculators
   |- MyCalc.ts
|- parsers
   |- XmlParser.ts
   |- JsonParser.ts
|- index.ts // currently holding export statements
Run Code Online (Sandbox Code Playgroud)

我想做的事

我想要一个这样的index.ts文件:

// a top-level module, resolved like the `node` strategy of NodeJS module resolution
export module IndexModule {
    export { MyHelper } from "./helpers/MyHelper";
    export { AnotherHelper } from "./helpers/AnotherHelper";
    // do the same for every class in my project
}
Run Code Online (Sandbox Code Playgroud)

那么我可以执行以下操作:

import { JsonParser } from "IndexModule"; // no relative nor absolute path

export class MyHelper {
    public constructor(input: any){
       var result: any = new JsonParser().parse(input);
       // work with result ...
    }
}
Run Code Online (Sandbox Code Playgroud)

我在很多地方都看到过(在 SO 上,TypeScript 的 github 存储库,...),我并不是唯一一个在导入相对/绝对路径方面苦苦挣扎的人。

我的实际问题是:是否可以创建这样的模块(或机制),以便我可以在顶级容器中定义每个导出,并像使用 NodeJS 模块(如import * as moment from "moment")一样导入此容器?

我试过的

第一次尝试

这是index.ts我实际拥有的文件:

// index.ts is in the root directory of the project, it accesses classes with relative paths (but here, management is quite easy)
export { Constants } from "./helpers/Constants";
export { ConstraintCalculatorHelper } from "./helpers/ConstraintCalculatorHelper";
export { MomentHelper } from "./helpers/MomentHelper";
export { Formatter, OutputFormatterHelper } from "./helpers/OutputFormatterHelper";
// ... doing this for each class I have in my project
Run Code Online (Sandbox Code Playgroud)

第二次尝试

declare module IndexModule {
    export { Constants } from "./helpers/Constants";
    export { ConstraintCalculatorHelper } from "./helpers/ConstraintCalculatorHelper";
    export { MomentHelper } from "./helpers/MomentHelper";
    export { Formatter, OutputFormatterHelper } from "./helpers/OutputFormatterHelper";
    // error : Export declarations are not permitted in a namespace
}
Run Code Online (Sandbox Code Playgroud)

第三、四、N次尝试

试过declare namespaceexport module等等等等。没有运气。

也试过declare module "IndexModule,导致报错:环境模块声明中的导入或导出声明无法通过相对模块名引用模块

边注

我不是 NodeJS / 模块 / 命名空间方面的专家,所以我可能不太了解一些东西。如果是这样,如果有人能指出我的误解,我将不胜感激。

此处显示实际问题的相关链接。

配置 :

  • NodeJS v7.2
  • 打字稿 v2.2

tsconfig.json :

{
  "compileOnSave": true,
  "compilerOptions": {
    "removeComments": false,
    "sourceMap": true,
    "module": "commonjs",
    "target": "es6",
    "noImplicitAny": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "outDir": "./bin/LongiCalc"
  }
}
Run Code Online (Sandbox Code Playgroud)

Jac*_*cks 1

我终于得到了我想要的东西。以下是步骤和相关说明。

1. 架构示例

为了说明问题/解决方案,让我们以这个树状结构为例

root
  \- A
     \- a1.ts
     \- a2.ts
  \- B
     \- D
        \- bd1.ts
        \- bd2.ts
     \- b1.ts
  \- C
     \- c1.ts
     \- c2.ts
Run Code Online (Sandbox Code Playgroud)

有一个根文件夹,包含其他 3 个文件夹(A、B 和 C):

  • A 包含两个文件 (a1.tsa2.ts)
  • B 包含一个包含两个文件的文件夹(D,带有bd1.tsbd2.ts)和一个文件 ( b1.ts)
  • C 包含两个文件 (c1.tsc2.ts)

所以现在,如果bd1.ts需要在 中声明的类a1.ts,我们目前将执行 ..

import { ClassFromA1 } from "../../A/a1"
Run Code Online (Sandbox Code Playgroud)

..它使用相对路径并且难以维护(IMO)。我们将在接下来的步骤中摆脱它。

2. 创建一个index.ts包含所有导入的

index.ts该文件(我这样命名,您可以选择任何您想要的名称)的目的是将所有需要的类保留在一个文件中。

因此,我们要做的就是在目录中的树状结构顶部创建此文件node_modules,并导出项目中所需的每个类。这样,我们只需要该index.ts文件来检索我们想要的类。

以下是内容:

// current position : root/node_modules/index.ts
export { ClassFromA1 }  from "./A/a1.ts"
export { ClassFromA2 }  from "./A/a2.ts"
export { ClassFromBD1 } from "./B/D/bd1.ts"
export { ClassFromBD2 } from "./B/D/bd2.ts"
export { ClassFromB1 }  from "./B/b1.ts"
export { ClassFromC1 }  from "./C/c1.ts"
export { ClassFromC2 }  from "./C/c2.ts"
Run Code Online (Sandbox Code Playgroud)

现在我们已经得到了这个,之前的导入可以这样完成..

import { ClassFromA1 } from "../../index"
Run Code Online (Sandbox Code Playgroud)

..但这仍然意味着我们使用相对路径,而不是最佳路径。

在项目中设置arootDirroot文件夹可以解决这个问题..

import { ClassFromA1 } from "index"
Run Code Online (Sandbox Code Playgroud)

..它有效!但问题发生在转译类上,其中路径在编译后不会解析为相对路径。

这意味着您的a1.js(编译a1.ts文件)仍将具有导入集,但可能是错误的,因为它不了解rootDir.

// a1.js
const index_1 = require("index") // supposed to be in the same package than the current file
Run Code Online (Sandbox Code Playgroud)

3. 选择node resolution策略

幸运的是,NodeJS 有一个node resolution可以设置的策略来解析节点模块导入。以下是可以在我们的项目中设置的Node 模块解析策略的非常简短的摘要:

  • 检查导入路径
  • 如果导入路径以.../=> 开头,则它是相对路径
  • 如果导入路径以名称开头 => 它是一个节点模块(这很重要

NodeJS 中模块的解析如下(总是从 开始bs1.ts):

  • 检查是否root/B/D/node_modules/index.ts存在 > 否
  • 检查是否root/B/node_modules/index.ts存在 > 否
  • 检查是否root/node_modules/index.ts存在 > 是!

我们在这里所做的是,我们欺骗 Typescript 认为这index.ts是一个 NodeJS 模块,并且必须将其解析为一个模块。

它将使用上面(和链接中)描述的模式来检索index.ts,然后我们可以从中导入所需的类,而无需相对/绝对路径。

4. 按照我们的意愿进行导入

现在可以通过以下方式进行导入..

// no matter in which file we are
import { ClassFromA1 } from "index";
import { ClassFromBD1 } from "index";
// and so on ..
Run Code Online (Sandbox Code Playgroud)

..并且它工作得很好,VisualStudio 上没有错误,也没有转译的类。欢迎对此解决方案提出任何评论。

干杯!