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

时间:2014-11-15 17:09:49

标签: javascript oop typescript extends

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

对象接口:

module Game.Core {
    export interface IObject {}

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

我的问题是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;
    }
}

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

所以,让我们以纹理为例。这是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);
        }
    }
}

当我执行this.add()时,我希望Game.Managers.Manager add()方法调用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 {

    }
}

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

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

11 个答案:

答案 0 :(得分:57)

TypeScript中有一个鲜为人知的功能,允许您使用Mixins创建可重复使用的小对象。您可以使用多重继承将这些对象组合成更大的对象(类不允许多重继承,但mixins允许这样做 - 它们与相关实现的接口类似)。

More information on TypeScript Mixins

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

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

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

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

然后是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];
            }
        });
    }); 
}

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

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

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

var being = new Being();

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

答案 1 :(得分:15)

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

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

可以在TS Playground site上查看此方法。

以下是实施:

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();

答案 2 :(得分:8)

不幸的是,typescript不支持多重继承。因此,没有完全无关紧要的答案,您可能需要重新构建您的程序

以下是一些建议:

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

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

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

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

一个提示:Typescript提供属性,这是getter和setter的语法糖。您可能需要查看此内容:http://blogs.microsoft.co.il/gilf/2013/01/22/creating-properties-in-typescript/

答案 3 :(得分:3)

一个非常棘手的解决方案是遍历要继承的类,方法是将函数一个接一个地添加到新的父类中

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

现在可以从ChildA类访问ChildBParent的所有属性,但是将无法识别它们,这意味着您将收到诸如Property 'x' does not exist on 'typeof Parent'的警告< / p>

答案 4 :(得分:2)

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;
}

答案 5 :(得分:1)

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

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

interface IBar {
  doBarThings(): void;
}

interface IBazz {
  doBazzThings(): void;
}

class Foo implements IBar, IBazz {}

现在,我们必须将实现添加到Foo类中。我们可以使用还实现以下接口的类mixin:

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!");
    }
  };
}

使用类mixins扩展Foo类:

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

答案 6 :(得分:1)

在设计模式中,有一个原则称为“偏重于继承而不是继承”。它说而不是从类A继承类B,而是将类B内的类A的实例作为属性,然后可以在类B内使用类A的功能。 您可以看到有关herehere的一些示例。

答案 7 :(得分:1)

就我而言,我使用了串联继承。 也许对某人来说这种方式会有所帮助:

class Sprite {
  x: number;
  y: number;

  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

class Plane extends Sprite {
  fly(): string {
    return 'I can Fly!'
  }
}

class Enemy {
  isEnemy = true;
}

class Player {
  isPlayer = true;
}

// You can create factory functions to create new instances
const enemyPlane = Object.assign(new Plane(1, 1), new Enemy());
const playerPlane = Object.assign(new Plane(2, 2), new Player());

另外我推荐阅读 Eric Elliott 的关于 js 继承的文章:

  1. The Heart & Soul of Prototypal OO: Concatenative Inheritance
  2. 3 Different Kinds of Prototypal Inheritance

答案 8 :(得分:0)

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

答案 9 :(得分:0)

这里已经有很多好的答案,但我只想举一个例子,您可以为正在扩展的类添加其他功能;

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);

答案 10 :(得分:-1)

如果您不喜欢使用多重继承,请一起使用 extendsimplements 以确保安全。

class C extends B implements A {
  // implements A here
}
相关问题