如何将带有循环依赖的typescript类移动到单独的文件中

时间:2018-04-09 07:23:28

标签: angular typescript

在一个角度项目中,我们有一个基类,从中可以派生出几个具体的类。在少数情况下,这些课程以循环方式相互引用。

当这些文件位于不同的类中时,项目会成功编译,但无法在浏览器中运行。

当所有类都被带到一个.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 __());
    };
})();

3 个答案:

答案 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'
  }
}