Typescript Inheritance across CommonJS modules - C

2019-08-10 08:14发布

I cannot get inheritance to work across CommonJS modules generated using typescript 1.0 (tscis run using --module commonjs)

This fails when two classes inheriting the same base class, call each other "through" the base class.

What seems to hapeen is that the first class imports the Base Class, which imports the second class which also imports the Base Class but the last Base Class import is failing.

An example illustrating this behaviour is provided below.

Is there anything in the Typescript or the CommonJS specifications preventing me from doing this, or is this a bug ?

=== Example ===

This fascinating piece of software is failed by running Lower.test.ts. What it simply tries to achieve is loading a word into Lower which keeps it in lower case, then using the inherited toUpper() method from the Base class, transforms it in upper case using the Upper class (which also inherits 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

2条回答
何必那么认真
2楼-- · 2019-08-10 08:55

After some research and testing, this ends up being a case of circular dependencies.

The issue is pretty well documented and various solutions have been proposed:

  1. Exporting before requiring: here
  2. Delaying the require or using injection: here
  3. Moving statements around: a pretty long list here

Unfortunately, there is very little control on where import and export statements can be set in Typescript which reduces solutions to injection only. The rewritten Base class is provided below.

Circular dependencies are a pain in Javascript modular projects. Typescript somehow makes things worse. That is bad news for a language supposed to address large projects

EDIT I have opened a case and submitted a proposed transpiler fix to the TypeScript project: 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
查看更多
Fickle 薄情
3楼-- · 2019-08-10 08:55

It is usually best to depend in just one direction - as per SOLID principles.

However, as you always need both Base and Upper (i.e. you cannot have one without the other) you can add them to the same module...

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());
查看更多
登录 后发表回答