SystemJS 中的 TypeScript 继承和循环依赖
TypeScript inheritance and circular dependencies in SystemJS
我在一个非常大的项目中使用带有 --module system
(SystemJS) 的 TypeScript。 SystemJS 支持循环依赖,并且大多数时候它工作正常。然而,当涉及到 TypeScript 继承时,事情就开始崩溃了。
例如,如果 class A
依赖于 class B
,并且 class B
从 class A
继承 ,那么如果 class A
首先加载:
- 它将暂停
class A's
解析并尝试加载 class B
依赖项
class B
会认为其依赖项已解决,因为 class A
已被触及。
class B's
继承将无法解析,因为 class A
仍未定义。
我在网上找到的大多数 "solutions" 与模块加载器的循环依赖关系是:
- 更改您的设计/将 类 组合成一个模块
- CommonJS 和非 TypeScript 特定的解决方法
我觉得循环设计有合理的理由,并且将 类 组合成巨型文件并不总是可取的,所以请考虑这些变通方法与我提出的问题无关。
有没有解决实际问题的方法?
改变你的设计是最有利的解决方案。 class 不应依赖于其子class。如果您在工厂左右使用它们,那是一个单独的问题,应该放在单独的 class/function/module.
中
Are there any solutions to the actual problem?
正如你所说,只有在先加载模块A时才会出现这个问题。解决方案是防止这种情况发生,并编写一个额外的模块作为 A 及其所有子class的代理,同时以正确的顺序导入它们。
在这种情况下,我建议您通过创建一个单独的接口 I
来删除对 A -> B
的依赖。 A
和B
都需要知道I
,B
需要实现
在加载过程中,B
必须告诉 A
在哪里可以找到 I
的构造函数或工厂(由 B
实现)。这将为您留下这些依赖项:
A -> I
B -> I
B -> A
界面 I
可能如下所示:
interface I {
bFoo(): void;
}
export default I;
Class A
可能看起来像这样:
import I from "./i";
class A {
private static __ICtor : new() => I;
public static setIConstructor(ctor: new() => I) {
A.__ICtor = ctor;
}
private __atSomePoint() : I {
return new A.__ICtor();
}
}
export default A;
最后 class B
:
import I from "./i";
import A from "./a";
class B extends A implements I {
public bFoo() {}
}
A.setIConstructor(B);
恕我直言,这将解决您的循环依赖,即使现在为时已晚。
使用 Babel 转换插件可以解决这个问题:https://github.com/zertosh/babel-plugin-transform-inline-imports-commonjs
它所做的是将文件开始时的模块导入转换为内联,这实际上只需要 import/require 其他模块在使用之前。
在大多数情况下,这会自动解决问题,因为到任何 class-using 代码实际运行时,模块都已完成导出。
请注意,默认情况下,上面的插件适用于项目中的所有导入,将它们全部转换为内联需求。然而,一个更严重的问题是我找不到一种内置的方法来使其与相对路径 imports/requires.
一起工作
我在我的项目分支中修复了这两个问题:https://github.com/Venryx/babel-plugin-transform-inline-imports-commonjs
我对这些更改提出了拉取请求,但它正在等待审查 atm。
我在一个非常大的项目中使用带有 --module system
(SystemJS) 的 TypeScript。 SystemJS 支持循环依赖,并且大多数时候它工作正常。然而,当涉及到 TypeScript 继承时,事情就开始崩溃了。
例如,如果 class A
依赖于 class B
,并且 class B
从 class A
继承 ,那么如果 class A
首先加载:
- 它将暂停
class A's
解析并尝试加载class B
依赖项 class B
会认为其依赖项已解决,因为class A
已被触及。class B's
继承将无法解析,因为class A
仍未定义。
我在网上找到的大多数 "solutions" 与模块加载器的循环依赖关系是:
- 更改您的设计/将 类 组合成一个模块
- CommonJS 和非 TypeScript 特定的解决方法
我觉得循环设计有合理的理由,并且将 类 组合成巨型文件并不总是可取的,所以请考虑这些变通方法与我提出的问题无关。
有没有解决实际问题的方法?
改变你的设计是最有利的解决方案。 class 不应依赖于其子class。如果您在工厂左右使用它们,那是一个单独的问题,应该放在单独的 class/function/module.
中Are there any solutions to the actual problem?
正如你所说,只有在先加载模块A时才会出现这个问题。解决方案是防止这种情况发生,并编写一个额外的模块作为 A 及其所有子class的代理,同时以正确的顺序导入它们。
在这种情况下,我建议您通过创建一个单独的接口 I
来删除对 A -> B
的依赖。 A
和B
都需要知道I
,B
需要实现
在加载过程中,B
必须告诉 A
在哪里可以找到 I
的构造函数或工厂(由 B
实现)。这将为您留下这些依赖项:
A -> I
B -> I
B -> A
界面 I
可能如下所示:
interface I {
bFoo(): void;
}
export default I;
Class A
可能看起来像这样:
import I from "./i";
class A {
private static __ICtor : new() => I;
public static setIConstructor(ctor: new() => I) {
A.__ICtor = ctor;
}
private __atSomePoint() : I {
return new A.__ICtor();
}
}
export default A;
最后 class B
:
import I from "./i";
import A from "./a";
class B extends A implements I {
public bFoo() {}
}
A.setIConstructor(B);
恕我直言,这将解决您的循环依赖,即使现在为时已晚。
使用 Babel 转换插件可以解决这个问题:https://github.com/zertosh/babel-plugin-transform-inline-imports-commonjs
它所做的是将文件开始时的模块导入转换为内联,这实际上只需要 import/require 其他模块在使用之前。
在大多数情况下,这会自动解决问题,因为到任何 class-using 代码实际运行时,模块都已完成导出。
请注意,默认情况下,上面的插件适用于项目中的所有导入,将它们全部转换为内联需求。然而,一个更严重的问题是我找不到一种内置的方法来使其与相对路径 imports/requires.
一起工作我在我的项目分支中修复了这两个问题:https://github.com/Venryx/babel-plugin-transform-inline-imports-commonjs
我对这些更改提出了拉取请求,但它正在等待审查 atm。