如何将循环依赖的打字稿类移动到单独的文件中

Ali*_*eza 4 typescript angular

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

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

当所有类都放在一个.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();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

和类似的sub-class-2.ts。我在cli cli控制台中收到以下警告:

WARNING in Circular dependency detected:
src/models/sub-class-1.ts -> src/models/base-class.ts -> src/models/sub-class-1.ts
Run Code Online (Sandbox Code Playgroud)

并在浏览器控制台中:

[WDS] Warnings while compiling.

Circular dependency detected:
src/models/base-class.ts -> src/models/sub-class-1.ts -> src/models/base-class.ts
Run Code Online (Sandbox Code Playgroud)

也就是说,代码正在运行。我可以将load fromModel方法移到一个访问者类(我在项目中实际上已经拥有了),但是这样我就无法从重载和局部性中受益(加载逻辑在物理上接近所有相关代码)。

但是,原始代码库创建以下错误:

Uncaught TypeError: Object prototype may only be an Object or null: undefined
at setPrototypeOf (<anonymous>)
at __extends (navigation-list-filter-node.ts:7)
Run Code Online (Sandbox Code Playgroud)

__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 __());
    };
})();
Run Code Online (Sandbox Code Playgroud)

smn*_*brv 7

问题在于,Webpack(由Angular CLI使用)对进口具有严格的规则(按其体系结构)。导入的顺序很重要,如果最终的JavaScript代码具有循环依赖性(模块1导入模块2,而模块2导入模块1),则导入机制将被破坏。

有趣的是,并非每次导入一些循环依赖关系都会成为问题。主要标准是:不要让导入的循环依赖关系传递给JavaScript。这意味着:将它们用作类型,但不要与它们进行比较,不要调用它们,不要将它们作为函数参数传递等等。

有效使用循环依赖关系(不会导致错误):

const a: SubClass1 = this; // type casting -> TS only
const b = <SubClass1>this; // type casting -> TS only
Run Code Online (Sandbox Code Playgroud)

为什么有效?因为在TypeScript编译后,SubClass1它将消失:JavaScript没有类型声明。

用法无效(导致循环依赖性错误):

const c = instanceof SubClass1 // passing as argument -> JS
const d = new SubClass1() // calling -> JS
const e = SubClass1 // saving as value -> JS
Run Code Online (Sandbox Code Playgroud)

现在回到您的问题:

doSomething() {
  if (this.children[0] instanceof SubClass1) {
    (this.children[0] as SubClass1).action1();
  }
}
Run Code Online (Sandbox Code Playgroud)

确切地说,它仅在这一行,if (this.children[0] instanceof SubClass1) {因为在打字稿编译后,将在下面强制转换类型的行被切除。instanceof基类中的in是循环依赖的典型原因,有多种解决方法。通常,您需要摆脱对instanceof其他事物的支持。

解决方案1.在每个子类上添加一个限定符属性,并强制将其实现:

export abstract class BaseClass {

  abstract get qualifier(): string;

  doSomething() {
    if (this.qualifier === 'SubClass1') { ... }
  }

}

export class SubClass1 extends BaseClass {

  get qualifier() {
    return 'SubClass1';
  }

}
Run Code Online (Sandbox Code Playgroud)

丑陋,但功能强大。如果有很多班级,您需要随时进行区分,则可以提供帮助。

解决方案2.添加一个boolean限定符,该限定符清楚地描述了一个特定的意图(典型示例是某些类的组需要与超类中实现的功能相同),并使用一些默认值实例化它:

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 {}
Run Code Online (Sandbox Code Playgroud)

第二个解决方案要好一些,因为限定不是由超类完成的,而是由子类本身完成的,因此子类以更细化的方式定义了谁/什么。在这种情况下,超类唯一要做的就是基于功能属性决定要做什么。

解决方案3.只需将逻辑移到子类中即可;让他们每个人执行其逻辑

export abstract class BaseClass {
  children: BaseClass[];

  abstract loadFromModel(model: StorageModel);

  abstract doSomething(): void;
}

export class SubClass1 extends BaseClass {
  action1() {
  }

  doSomething() {
    this.action1();
  }
}
Run Code Online (Sandbox Code Playgroud)

这是最琐碎的,但是并不总是足够的。

  • 哇。这就是我所说的完美 SO 答案。它解释了导致问题的机制和解决方法。我会更深入地研究答案,稍后再回来。 (3认同)