跨 CommonJS 模块的 Typescript 继承 - 循环依赖

Posted

技术标签:

【中文标题】跨 CommonJS 模块的 Typescript 继承 - 循环依赖【英文标题】:Typescript Inheritance across CommonJS modules - Circular Dependencies 【发布时间】:2014-08-28 07:11:54 【问题描述】:

我无法在使用 typescript 1.0 生成的 CommonJS 模块之间进行继承(tscis run using --module commonjs

当两个类继承同一个基类,“通过”基类相互调用时,这会失败。

似乎是第一个类导入了基类,它导入了第二个类,第二个类也导入了基类,但最后一个基类导入失败。

下面提供了一个说明此行为的示例。

Typescript 或 CommonJS 规范中是否有任何内容阻止我这样做,或者这是一个错误?

=== 示例 ===

这个迷人的软件因运行Lower.test.ts 而失败。它试图实现的只是将一个单词加载到Lower 中,使其保持小写,然后使用从Base 类继承的toUpper() 方法,使用Upper 类将其转换为大写(这也是继承Base)

Lower.test.ts

import Lower = require('./Lower')

console.log(new Lower('smallcaps').toUpper())

Base.ts

import Upper = require('./Upper')

class Base 

    word: string

    toUpper(): string 
        return new Upper(this.word).word
    


export = Base

Upper.ts

import Base = require('./Base')

class Upper extends Base 

    constructor(word:string) 
        super()
        this.word = word.toUpperCase()
    


export = Upper

Lower.ts

import Base = require('./Base')

class Lower extends Base 

    constructor(word:string) 

        super()
        this.word = word.toLowerCase()
    


export = Lower

【问题讨论】:

【参考方案1】:

通常最好只依赖一个方向 - 根据 SOLID 原则。

但是,由于您始终需要 BaseUpper(即不能没有另一个),您可以将它们添加到同一个模块中...

base.ts

export class Base 
    word: string

    toUpper(): string 
        return new Upper(this.word).word
    


export class Upper extends Base 

    constructor(word: string) 
        super()
        this.word = word.toUpperCase()
    

lower.ts

import b = require('base')

class Lower extends b.Base 

    constructor(word: string) 

        super()
        this.word = word.toLowerCase()
    


export = Lower 

app.ts

import Lower = require('lower');

console.log(new Lower('smallcaps').toUpper());

【讨论】:

感谢您的解决方法;我可以接受这样一个事实,即它降低了模块化,但它仍然留下了悬而未决的问题。至于朝一个方向发展,这并不总是有意义的:想象一下这个非常简单的几何应用程序,它实现了Vector。与许多其他几何图形一样,Vector 可以通过将其乘以另一个 Vector 来进行缩放。创建一个Scalable 类是有意义的,该类Vector 继承,并实现了一个scale() 方法,该方法有一个Vector 作为参数。到这里:继承一个在方法中使用其子类之一的类。 我还是会避免它。如果 Vector 总是可以缩放,为什么 Scalable 是子类。如果不能总是缩放,就不应该有缩放方法,缩放方法应该由Scalable子类添加。【参考方案2】:

经过一些研究和测试,这最终是一个循环依赖的情况。

这个问题已经得到很好的记录,并且已经提出了各种解决方案:

    requiring:here之前导出 延迟require 或使用注入:here 移动语句:很长的列表here

不幸的是,在 Typescript 中可以设置 importexport 语句的位置几乎没有控制,这减少了仅注入的解决方案。下面提供了重写的Base 类。

循环依赖是 javascript 模块化项目中的一个难题。打字稿不知何故使事情变得更糟。对于本应处理大型项目的语言来说,这是个坏消息

编辑 我已经打开了一个案例并向 TypeScript 项目提交了一个建议的转译器修复:here

Base.ts

//This import will "fail" by importing an empty object but is needed to trick the compiler
//An alternative is to design and Upper.d.ts definition file and reference it
import Upper = require('./Upper')

class Base 

    //Upper is a sub-class of Base and needs to be injected to be instantiated
    private _Upper: typeof Upper

    word: string

    constructor(_Upper: typeof Upper) 
        this._Upper = _Upper
    

    toUpper(): string 
        //This where the injection is used
        return new this._Upper(this.word).word
    


    getUpperWord(upper: Upper): string 
        //The injection is not reauired in this case
        return upper.word
    


export = Base

Upper.ts

import Base = require('./Base')

class Upper extends Base 

    constructor(word:string) 
        super(Upper)
        this.word = word.toUpperCase();
    


export = Upper

【讨论】:

以上是关于跨 CommonJS 模块的 Typescript 继承 - 循环依赖的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript 之 声明文件的结构

编译TypeScript(TypeScript转JavaScript)

将 CommonJS 默认导出导入为命名导出/无法加载 ES 模块

Typescript和Node模块解析策略

从0开始的TypeScriptの十一:模块和命名空间

从0开始的TypeScriptの十一:模块和命名空间