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.
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.
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.
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.
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();
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With