Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: How to extend two classes?

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

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 getKeyInManager and setKeyInManager will not change and I want to reuse it, not to duplicate it, here is the implementation:

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 inside the object itself in its property _keyInManager.

So, let's take an example with a Texture. Here goes the 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.Manager add() 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:

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

I know that IManagedObject is an interface and cannot contain implementation, but I don't know what to write to inject the class ObjectThatShouldAlsoBeExtended inside my Texture class. Knowing that the same process would be required for Sprite, TilingSprite, Layer and more.

I need experienced TypeScript feedback/advice 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.

like image 896
Vadorequest Avatar asked Nov 15 '14 17:11

Vadorequest


People also ask

Can we extend two classes in TypeScript?

To extend two classes with TypeScript, we merge all the properties in each class' prototype into the class that we want to inherit from the two classes.

How do I extend a class in TypeScript?

Just like object-oriented languages such as Java and C#, TypeScript classes can be extended to create new classes with inheritance, using the keyword extends . In the above example, the Employee class extends the Person class using extends keyword.

Can we extend two classes in class?

This means you can't extend two or more classes in a single class. When you need to extend two or more classes in Java, you need to refactor the classes as interfaces. This is because Java allows implementing multiple interfaces on a single class.


2 Answers

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

More information on TypeScript Mixins

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:

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

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:

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(); 
like image 77
Fenton Avatar answered Oct 05 '22 23:10

Fenton


I would suggest using the new mixins approach described there: https://blogs.msdn.microsoft.com/typescript/2017/02/22/announcing-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.

This approach might be checked on the TS Playground site.

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(); 
like image 27
Mark Dolbyrev Avatar answered Oct 05 '22 23:10

Mark Dolbyrev