在一个角度项目中,我们有一个基类,从中可以派生出几个具体的类。在少数情况下,这些课程以循环方式相互引用。
当这些文件位于不同的类中时,项目会成功编译,但无法在浏览器中运行。
当所有类都被带到一个.ts
文件时,一切正常,但我们有一个很长的,难以维护的文件。
有没有办法分隔这些文件?即使是合并文件的现有预编译任务(以智能方式)也将受到赞赏。
编辑: 林'熟悉导入,模块等。问题是循环依赖。而且,它可以通过更多的抽象来优雅地处理。我尝试添加一个最小的再现代码。
编辑: 这是一个复制品,创造了一个警告:它还说明了我开始具有循环依赖性的原因。
base-class.ts:
import {StorageModel} from './storage-model';
import {SubClass1} from './sub-class-1';
export abstract class BaseClass {
children: BaseClass[];
abstract loadFromModel(model: StorageModel);
doSomething() {
if (this.children[0] instanceof SubClass1) {
(this.children[0] as SubClass1).action1();
}
}
}
sub-class-1.ts:
import {BaseClass} from './base-class';
import {StorageModel} from './storage-model';
import {SubClass2} from './sub-class-2';
export class SubClass1 extends BaseClass {
title: string = 'hello';
action1() {
}
loadFromModel(model: StorageModel) {
(this.children[0] as SubClass2).action2();
}
}
和类似的`sub-class-2.ts。 我在angular cli控制台中收到以下警告:
WARNING in Circular dependency detected:
src/models/sub-class-1.ts -> src/models/base-class.ts -> src/models/sub-class-1.ts
并在浏览器控制台中:
[WDS] Warnings while compiling.
Circular dependency detected:
src/models/base-class.ts -> src/models/sub-class-1.ts -> src/models/base-class.ts
那说,代码正在运行。
我可以将加载fromModel
方法移动到访问者类(我实际上在项目中),但是我不会从重载和位置中受益(加载逻辑在物理上接近所有相关代码)。
但是,原始代码库会产生以下错误:
Uncaught TypeError: Object prototype may only be an Object or null: undefined
at setPrototypeOf (<anonymous>)
at __extends (navigation-list-filter-node.ts:7)
__extends
函数不是我的代码:
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype =
b.prototype, new __());
};
})();
答案 0 :(得分:4)
问题是Webpack(由Angular CLI使用)对导入(通过其体系结构)有严格的规则。导入的顺序很重要,如果最终的JavaScript代码具有循环依赖关系(模块1导入模块2导入模块1),则导入机制会中断。
有趣的是,并非每次导入一些循环依赖都会成为一个问题。主要标准是:不要让导入的循环依赖关系通过JavaScript。这意味着:将它们用作类型,但不要与它们进行比较,不要调用它们,不要将它们作为函数参数传递等等。
循环依赖的有效用法(不会导致错误):
const a: SubClass1 = this; // type casting -> TS only
const b = <SubClass1>this; // type casting -> TS only
为什么它有效?因为在编译TypeScript之后,这个SubClass1
将消失:JavaScript没有类型声明。
使用无效(导致循环依赖性错误):
const c = instanceof SubClass1 // passing as argument -> JS
const d = new SubClass1() // calling -> JS
const e = SubClass1 // saving as value -> JS
现在回到你的问题:
doSomething() {
if (this.children[0] instanceof SubClass1) {
(this.children[0] as SubClass1).action1();
}
}
准确地说,它仅在此行if (this.children[0] instanceof SubClass1) {
处,因为在打字类型下面的行将在打字稿编译后被切断。基类中的instanceof
是循环依赖的典型原因,有多种方法可以解决它。大多数情况下,你需要摆脱instanceof
而不是其他东西。
解决方案1。在每个子类上添加限定符属性并强制执行:
export abstract class BaseClass {
abstract get qualifier(): string;
doSomething() {
if (this.qualifier === 'SubClass1') { ... }
}
}
export class SubClass1 extends BaseClass {
get qualifier() {
return 'SubClass1';
}
}
丑陋但功能齐全。如果有很多课程,你需要在任何时刻区分所有课程。
解决方案2。添加例如布尔限定符,它清楚地描述了一个特定的意图(典型的例子是某些类需要在超类中实现的相同特性),并使用一些默认值对其进行实例化:
export abstract class BaseClass {
get isDuck() {
return false; // by default is not a duck
}
doSomething() {
if (this.isDuck) { ... }
}
}
// this one is a duck
export class SubClass1 extends BaseClass {
get isDuck() {
return true;
}
}
// this one is not a duck. Why the hell should it be a duck? :D
export class SubClass2 extends BaseClass {}
第二种解决方案稍微好一些,因为限定不是由超类完成的,而是由子类本身完成的,因此子类以更细化的方式定义了它/它是什么。在这种情况下,超类唯一能做的就是根据功能属性决定做什么。
解决方案3。只需将逻辑移到子类;让他们每个人实现其逻辑
export abstract class BaseClass {
children: BaseClass[];
abstract loadFromModel(model: StorageModel);
abstract doSomething(): void;
}
export class SubClass1 extends BaseClass {
action1() {
}
doSomething() {
this.action1();
}
}
这是最微不足道的,但并不总是足够的。
答案 1 :(得分:0)
打字稿加载器应该为您修复循环依赖关系。因此,将其拆分为多个文件应该没问题。 (只需使用import typescript loader就可以解决它,就像python一样)。
那就是说,大部分时间这不是一个好主意,并且表示你可能想要抽象一些东西(制作界面并引用它)或者你有多个类应该只是一个类。
答案 2 :(得分:0)
好吧,我正在将答案从我发布的其他线程复制粘贴到这里,因为我认为这是相关的,并且先前的答案并不能真正为问题提供简单的解决方案。
通过向基类中添加几个新方法以及一个附加的type属性,我设法删除了instanceof
检查并因此删除了循环依赖项(因为如前所述,编译代码时,类型定义也被删除了) )。
例如
class A {
constructor() {
this.type = 'A'
}
isInstanceOfA(): this is A {
return this.type === 'A'
}
isInstanceOfB(): this is B {
return this.type === 'B'
}
}
class B extends A {
constructor() {
super()
this.type = 'B'
}
}