Javascript 打字稿:如何扩展两个类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26948400/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 23:33:40  来源:igfitidea点击:

Typescript: How to extend two classes?

javascriptooptypescriptextends

提问by Vadorequest

I want to save my time and to reuse common code across classes which extends PIXI classes (a 2d webGl renderer library).

我想节省我的时间并在扩展 PIXI 类(一个 2d webGl 渲染器库)的类中重用公共代码。

Object Interfaces:

对象接口:

module Game.Core {
    export interface IObject {}

    export interface IManagedObject extends IObject{
        getKeyInManager(key: string): string;
        setKeyInManager(key: string): IObject;
    }
}

My issue is that the code inside getKeyInManagerand setKeyInManagerwill not change and I want to reuse it, not to duplicate it, here is the implementation:

我的问题是,里面的代码getKeyInManagersetKeyInManager不会改变,我想重新使用它,而不是复制它,这里是实现:

export class ObjectThatShouldAlsoBeExtended{
    private _keyInManager: string;

    public getKeyInManager(key: string): string{
        return this._keyInManager;
    }

    public setKeyInManager(key: string): DisplayObject{
        this._keyInManager = key;
        return this;
    }
}

What I want to do is to automatically add, through a Manager.add(), the key used in the manager to reference the object insidethe object itself in its property _keyInManager.

我想要做的是通过 a 自动添加Manager.add()管理器中使用的键,以在其 property 中引用对象本身内部的对象_keyInManager

So, let's take an example with a Texture. Here goes the TextureManager

因此,让我们以纹理为例。这里是TextureManager

module Game.Managers {
    export class TextureManager extends Game.Managers.Manager {

        public createFromLocalImage(name: string, relativePath: string): Game.Core.Texture{
            return this.add(name, Game.Core.Texture.fromImage("/" + relativePath)).get(name);
        }
    }
}

When I do this.add(), I want the Game.Managers.Manageradd()method to call a method which would exist on the object returned by Game.Core.Texture.fromImage("/" + relativePath). This object, in this case would be a Texture:

当我这样做时this.add(),我希望该Game.Managers.Manageradd()方法调用一个方法,该方法将存在于Game.Core.Texture.fromImage("/" + relativePath). 这个对象,在这种情况下将是一个Texture

module Game.Core {
    // I must extends PIXI.Texture, but I need to inject the methods in IManagedObject.
    export class Texture extends PIXI.Texture {

    }
}

I know that IManagedObjectis an interface and cannot contain implementation, but I don't know what to write to inject the class ObjectThatShouldAlsoBeExtendedinside my Textureclass. Knowing that the same process would be required for Sprite, TilingSprite, Layerand more.

我知道这IManagedObject是一个接口,不能包含实现,但我不知道写什么来ObjectThatShouldAlsoBeExtended在我的Texture类中注入类。知道SpriteTilingSpriteLayer等需要相同的过程。

I need an experienced TypeScript feedback/advices here, it must be possible to do it, but not by multiple extends since only one is possible at the time, I didn't find any other solution.

我在这里需要有经验的 TypeScript 反馈/建议,必须可以做到,但不能通过多个扩展,因为当时只有一个是可能的,我没有找到任何其他解决方案。

回答by Fenton

There is a little known feature in TypeScript that allows you to use Mixins to create re-usable small objects. You can compose these into larger objects using multiple inheritance (multiple inheritance is not allowed for classes, but it is allowed for mixins - which are like interfaces with an associated implenentation).

TypeScript 中有一个鲜为人知的功能,它允许您使用 Mixins 创建可重用的小对象。您可以使用多重继承将它们组合成更大的对象(类不允许多重继承,但 mixin 是允许的 - 这就像具有关联实现的接口)。

More information on TypeScript Mixins

有关 TypeScript Mixin 的更多信息

I think you could use this technique to share common components between many classes in your game and to re-use many of these components from a single class in your game:

我认为您可以使用这种技术在游戏中的许多类之间共享公共组件,并从游戏中的单个类中重用其中的许多组件:

Here is a quick Mixins demo... first, the flavours that you want to mix:

这是一个快速的 Mixins 演示......首先,你想要混合的口味:

class CanEat {
    public eat() {
        alert('Munch Munch.');
    }
}

class CanSleep {
    sleep() {
        alert('Zzzzzzz.');
    }
}

Then the magic method for Mixin creation (you only need this once somewhere in your program...)

然后是 Mixin 创建的神奇方法(你只需要在你的程序中的某个地方使用一次......)

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
             if (name !== 'constructor') {
                derivedCtor.prototype[name] = baseCtor.prototype[name];
            }
        });
    }); 
}

And then you can create classes with multiple inheritance from mixin flavours:

然后你可以从 mixin 风格创建具有多重继承的类:

class Being implements CanEat, CanSleep {
        eat: () => void;
        sleep: () => void;
}
applyMixins (Being, [CanEat, CanSleep]);

Note that there is no actual implementation in this class - just enough to make it pass the requirements of the "interfaces". But when we use this class - it all works.

请注意,此类中没有实际实现 - 足以使其通过“接口”的要求。但是当我们使用这个类时 - 一切正常。

var being = new Being();

// Zzzzzzz...
being.sleep();

回答by Mark Dolbyrev

I would suggest using the new mixins approach described there: https://blogs.msdn.microsoft.com/typescript/2017/02/22/announcing-typescript-2-2/

我建议使用那里描述的新混合方法:https: //blogs.msdn.microsoft.com/typescript/2017/02/22/annoucing-typescript-2-2/

This approach is better, than the "applyMixins" approach described by Fenton, because the autocompiler would help you and show all the methods / properties from the both base and 2nd inheritance classes.

这种方法比 Fenton 描述的“applyMixins”方法更好,因为自动编译器将帮助您并显示来自基类和第二个继承类的所有方法/属性。

This approach might be checked on the TS Playground site.

可以在TS Playground 站点上检查此方法。

Here is the implementation:

这是实现:

class MainClass {
    testMainClass() {
        alert("testMainClass");
    }
}

const addSecondInheritance = (BaseClass: { new(...args) }) => {
    return class extends BaseClass {
        testSecondInheritance() {
            alert("testSecondInheritance");
        }
    }
}

// Prepare the new class, which "inherits" 2 classes (MainClass and the cass declared in the addSecondInheritance method)
const SecondInheritanceClass = addSecondInheritance(MainClass);
// Create object from the new prepared class
const secondInheritanceObj = new SecondInheritanceClass();
secondInheritanceObj.testMainClass();
secondInheritanceObj.testSecondInheritance();

回答by lhk

Unfortunately typescript does not support multiple inheritance. Therefore there is no completely trivial answer, you will probably have to restructure your program

不幸的是,打字稿不支持多重继承。因此,没有完全微不足道的答案,您可能必须重组您的程序

Here are a few suggestions:

以下是一些建议:

  • If this additional class contains behaviour that many of your subclasses share, it makes sense to insert it into the class hierarchy, somewhere at the top. Maybe you could derive the common superclass of Sprite, Texture, Layer, ... from this class ? This would be a good choice, if you can find a good spot in the type hirarchy. But I would not recommend to just insert this class at a random point. Inheritance expresses an "Is a - relationship" e.g. a dog is an animal, a texture is an instance of this class. You would have to ask yourself, if this really models the relationship between the objects in your code. A logical inheritance tree is very valuable

  • If the additional class does not fit logically into the type hierarchy, you could use aggregation. That means that you add an instance variable of the type of this class to a common superclass of Sprite, Texture, Layer, ... Then you can access the variable with its getter/setter in all subclasses. This models a "Has a - relationship".

  • You could also convert your class into an interface. Then you could extend the interface with all your classes but would have to implement the methods correctly in each class. This means some code redundancy but in this case not much.

  • 如果这个额外的类包含您的许多子类共享的行为,那么将它插入到类层次结构的顶部某处是有意义的。也许你可以从这个类中派生出 Sprite、Texture、Layer 等通用超类?如果您能在类型层次结构中找到一个好位置,这将是一个不错的选择。但我不建议在随机点插入这个类。继承表达了“是-关系”,例如狗是动物,纹理是此类的一个实例。您必须问自己,这是否真的模拟了代码中对象之间的关系。一棵逻辑继承树很有价值

  • 如果附加类在逻辑上不适合类型层次结构,则可以使用聚合。这意味着您将此类类型的实例变量添加到 Sprite、Texture、Layer 的公共超类中,然后您可以在所有子类中使用其 getter/setter 访问该变量。这模拟了“具有 - 关系”。

  • 您还可以将您的类转换为接口。然后,您可以使用所有类扩展接口,但必须在每个类中正确实现方法。这意味着一些代码冗余,但在这种情况下并不多。

You have to decide for yourself which approach you like best. Personally I would recommend to convert the class to an interface.

你必须自己决定你最喜欢哪种方法。我个人建议将类转换为接口。

One tip: Typescript offers properties, which are syntactic sugar for getters and setters. You might want to take a look at this: http://blogs.microsoft.co.il/gilf/2013/01/22/creating-properties-in-typescript/

一个提示:Typescript 提供了属性,这些属性是 getter 和 setter 的语法糖。你可能想看看这个:http: //blogs.microsoft.co.il/gilf/2013/01/22/creating-properties-in-typescript/

回答by Ivan Castellanos

There is a new feature in JavaScript (ES7) called decorators, and using that feature plus a little library called typescript-mixyou can use mixins to have multiple inheritance with just a couple of lines

JavaScript (ES7) 中有一个叫做装饰器的新特性,使用这个特性加上一个叫做typescript-mix的小库,你可以使用 mixins 来实现多重继承,只需几行

// The following line is only for intellisense to work
interface Shopperholic extends Buyer, Transportable {}

class Shopperholic {
  // The following line is where we "extend" from other 2 classes
  @use( Buyer, Transportable ) this 
  price = 2000;
}

回答by nomadoda

I think there is a much better approach, that allows for solid type-safetyand scalability.

我认为有一种更好的方法,可以实现可靠的类型安全性和可扩展性。

First declare interfaces that you want to implement on your target class:

首先声明要在目标类上实现的接口:

interface IBar {
  doBarThings(): void;
}

interface IBazz {
  doBazzThings(): void;
}

class Foo implements IBar, IBazz {}

Now we have to add the implementation to the Fooclass. We can use class mixins that also implements these interfaces:

现在我们必须将实现添加到Foo类中。我们可以使用也实现这些接口的类 mixins:

class Base {}

type Constructor<I = Base> = new (...args: any[]) => I;

function Bar<T extends Constructor>(constructor: T = Base as any) {
  return class extends constructor implements IBar {
    public doBarThings() {
      console.log("Do bar!");
    }
  };
}

function Bazz<T extends Constructor>(constructor: T = Base as any) {
  return class extends constructor implements IBazz {
    public doBazzThings() {
      console.log("Do bazz!");
    }
  };
}

Extend the Fooclass with the class mixins:

Foo使用类 mixins扩展类:

class Foo extends Bar(Bazz()) implements IBar, IBazz {
  public doBarThings() {
    super.doBarThings();
    console.log("Override mixin");
  }
}

const foo = new Foo();
foo.doBazzThings(); // Do bazz!
foo.doBarThings(); // Do bar! // Override mixin

回答by WillCooter

A very hacky solution would be to loop through the class you want to inherit from adding the functions one by one to the new parent class

一个非常hacky的解决方案是循环遍历你想要继承的类,通过将函数一一添加到新的父类

class ChildA {
    public static x = 5
}

class ChildB {
    public static y = 6
}

class Parent {}

for (const property in ChildA) {
    Parent[property] = ChildA[property]
}
for (const property in ChildB) {
    Parent[property] = ChildB[property]
}


Parent.x
// 5
Parent.y
// 6

All properties of ChildAand ChildBcan now be accessed from the Parentclass, however they will not be recognised meaning that you will receive warnings such as Property 'x' does not exist on 'typeof Parent'

现在可以从类访问ChildA和 的所有属性,但是它们不会被识别,这意味着您将收到警告,例如ChildBParentProperty 'x' does not exist on 'typeof Parent'

回答by Tebo

There are so many good answers here already, but i just want to show with an example that you can add additional functionality to the class being extended;

这里已经有很多很好的答案,但我只想用一个例子来展示你可以向被扩展的类添加额外的功能;

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            if (name !== 'constructor') {
                derivedCtor.prototype[name] = baseCtor.prototype[name];
            }
        });
    });
}

class Class1 {
    doWork() {
        console.log('Working');
    }
}

class Class2 {
    sleep() {
        console.log('Sleeping');
    }
}

class FatClass implements Class1, Class2 {
    doWork: () => void = () => { };
    sleep: () => void = () => { };


    x: number = 23;
    private _z: number = 80;

    get z(): number {
        return this._z;
    }

    set z(newZ) {
        this._z = newZ;
    }

    saySomething(y: string) {
        console.log(`Just saying ${y}...`);
    }
}
applyMixins(FatClass, [Class1, Class2]);


let fatClass = new FatClass();

fatClass.doWork();
fatClass.saySomething("nothing");
console.log(fatClass.x);

回答by kenberkeley

I found an up-to-date & unparalleled solution: https://www.npmjs.com/package/ts-mixer

我找到了一个最新且无与伦比的解决方案:https: //www.npmjs.com/package/ts-mixer