TypeScript:多文件类+模块合并的替代方案
在我们现有的代码库中,我们使用 JavaScript 的“类是函数,函数是对象”这一特性来进行命名空间管理。
例如,假设我们有一个名为 Foo 的类,其中包含一个名为 Bar 的类。
Foo 是一个全局导出的对象(即,它存在于 window.Foo),并且 window.Foo.Bar 是 Bar 的构造函数。(我认为可以公平地说,这是一个常见的 JavaScript 模式)。
的确,这种模式会产生方向依赖。Foo 必须在 Bar 之前加载。我们使用 stealjs 来实现这一点,stealjs 是一个异步模块加载器,并不符合 AMD 协议。出于这个原因,我们不能使用 TypeScript 内置的 import/export AMD 代码生成。至少在我们花费数周时间将整个代码库迁移到 requirejs 之前,是不行的。
在 TypeScript 中匹配这个模式时,我们说有一个名为 Foo 的类,还有一个名为 Foo 的模块。
class Foo {
/* Foo's properties */
}
module Foo {
export class Bar {
/* Bar's properties */
}
}
这是合法的 TypeScript,并且它可以编译成上面描述的 JavaScript 情况:Foo 类对象将包含 Bar 类对象作为其“Bar”属性。Foo 模块被称为与 Foo 类合并,而 Bar 被称为内部类。
然而,如果我只是将这两个语句分成不同的文件:
// Foo.ts
class Foo {
/* Foo's properties */
}
// Foo/Bar.ts
module Foo {
export class Bar {
/* Bar's properties */
}
}
这不再是合法的 TypeScript,并且无法编译。[1](尽管我们可以并且会通过我们自己的模块加载器安排第二个文件在第一个文件加载后加载。)
这是我们的大问题,因为我们不打算把所有内部类声明压缩到一个文件中。据我所知,实际上没有解决方案。我们只能污染全局命名空间,不能将内部类与我们的命名空间合并。
如果我们使用 TS 内置的导入/导出语义,这个问题就不会出现,但我们没有使用,原因如上所述。现在把所有东西迁移到 requirejs 并不是可行的方案。考虑到我们的情况,你认为绕过类-模块合并限制的最佳策略是什么?
[1]TypeScript:模块 x 不能与不同文件中 x 的先前声明合并
答案#1
你可以逐步切换到 require.js 的 AMD,而不是一次性重写所有内容。实际上,最简单的方法是依赖 TypeScript 内置的 AMD 支持,因为它自动处理了很多你需要做的事情,并且 TypeScript 中的导出和导入语法非常容易使用。在 TypeScript 中,如果你没有在文件中添加 export 或 import 语句,这些文件会按原样编译;而使用 export/import 的文件会被编译为 AMD 模块。这里有一点学习曲线,因为你需要添加一个 require 配置,并开始在非 TypeScript 文件中使用 require(['mytsamd'], function (mytsamd) { /* 使用 mytsamd 做一些操作 */ }) 这个模式(或者使用类似 Q.js 的方法将其转换为 Promise),以引用 AMD 对象。