Multiple Inheritance

And

TypeScript Interfaces

Multiple Inerhitance

Just like it sounds..one object's prototype inheriting from multiple other objects prototoypes.

JavaScript doesn't natively support multiple inheritance, but we can use a design pattern called a mixin.

A mixin you say?

mixin is a design pattern where you iterate over multiple objects properties to combine them into a single new object. 

function extend(destination, source) {
  for (var k in source) {
    if (source.hasOwnProperty(k)) {
      destination[k] = source[k];
    }
  }
  return destination; 
}

And now you have multiple inheritance.

This method allows us to inherit from (extend) as many other object's as we want.

function extend(destination, source) {
  for (var k in source) {
    if (source.hasOwnProperty(k)) {
      destination[k] = source[k];
    }
  }
  return destination; 
}
function Taco(){

}
Taco.prototype.eat = function(){
    /*do stuff*/ 
};

function Beans(){

}
Beans.prototype.cook = function(){
    /*do stuff*/ 
};

function Guacomole(){

}
Guacomole.prototype.spread = function(){
    /*do stuff*/ 
};

extend(Taco.prototype, Beans.prototype);

console.log(Taco); 

By using the extend function to implement the mixin pattern we created references to the super prototype methods.

Also notice that we don't inherit a constructor with the mixin

Since we're adding methods to the objects existing prototype, without Object.create() to assign a new prototype object, the constructor remains intact.

var canFly = function() {
  this.price = 9.99;
  this.takeOff = function() {
    //fly baby
  };
  return this;
};

var isSubmersible = function() {
  this.oxygenTanks = 4;
  this.dive = function() {
      //go deep
    };
  return this;
}

var Car = function(opts) {
  this.wheels = 4;
  if (opts.fly) {
    canFly.call(Car.prototype);
  }
  if (opts.swim) {
    isSubmersible.call(Car.prototype);
  }
};

var flyingSubmaringCar = new Car({ fly : true, swim : true });
console.log(flyingSubmaringCar);

"A mixin should be a process not an object"

Although this achieves multiple inheritance best practice says:

"Something that adds behavior"

There are better, cleaner ways to inherit properties from another class thanks to ES6's Object.assign() method

 

We can use this to extend our functions more easier than building an extend function to do this for us

 

Remember, this only works on browsers that support ES6 at the bare minimum

 

Older browsers that do not support ES5 will require the use of a polyfill

function Taco(){

}
Taco.prototype.eat = function(){
    /*do stuff*/ 
};

function Beans(){

}
Beans.prototype.cook = function(){
    /*do stuff*/ 
};

function Guacomole(){

}
Guacomole.prototype.spread = function(){
    /*do stuff*/ 
};

Taco.prototype = Object.assign(Taco.prototype, Beans.prototype);

console.log(Taco); 

We'll use the Object.assign() method to extend our Taco's prototype with the contents of Beans instead of using the old extend function!

Interfaces (TypeScript)

Mixins allowed us to inherit behavior from multiple objects...

...Interfaces allow us to define a contract.

If any object implements an interface it must provide certain behavior and properties

If you wanna be in the band, you gotta wear the makeup bro...

Easiest Way To Remember  Interfaces 

//we could define an interface for a Car class such that 
//every car must have an engine and a color like this:

interface ICar{
    engine: string;
    color: string;
}



//The Car class adheres to the interface ICar because it implements ICar

class Car implements ICar {
    constructor (public engine: string, public color: string) {
    }
} 


//Create a new instance of a car and fulfills all requirements of the ICar interface 'contract'
var hondaAccord = new Car('V6', 'green');
alert(hondaAccord.engine);

Remember an interface is "just a contract".

By abiding by the contract your object will implement all the requirements of the contract

 

http://www.typescriptlang.org/Playground/

class Car {
    private _basePrice: number;
    engine: IEngine;

    constructor(options: IAutoOptions) {
        this._basePrice = options.basePrice;
        this.engine = options.engine;
    }
    
    drive() : number {
        console.log('driving');
		return 1000;
    }

    get price(): number {
        return this._basePrice;
    }

    set price(value: number) {
        this._basePrice = value;
    }
} 

Implementing interfaces on subclasses

interface IEngine {
    start() : void;
    stop() : void;
}

interface IAutoOptions {
    engine: IEngine;
    basePrice: number;
}

interface ICanFly {
  altitude: number;
  fly() : void;
}

class FlyingEngine implements IEngine { 
    start() : void { }
    stop() : void { }
}

class FlyingCar extends Car implements ICanFly {
	altitude: number;
	
  constructor(options: IAutoOptions){
    super(options);
    this.altitude = 0;
  };
  fly() : void {
    this.engine.start();
    console.log('flying');
  }

}

//Create a new flying car
var fc = new FlyingCar({basePrice: 1000, 
    engine: new FlyingEngine()})

alert(fc.price); //[Alert] 1000
class Bird implements ICanFly {
	name: string;
	altitude: number;
	
	constructor (name: string){
		this.name = name;
		this.altitude = 100;
	};
	fly() : void {
    console.log('flying');
  }
}

var fc = new FlyingCar({basePrice: 1000, engine: new FlyingEngine()})

var tweety = new Bird('Tweety');

var polly = new Bird('Polly');

var flyingThings: Array<ICanFly> = [fc, tweety, polly];

for (var i=0; i< flyingThings.length; i++) {
	alert(flyingThings[i].altitude);
} 

Verifying type with implementations

Resources

Multiple Inheritance

By DevLeague Coding Bootcamp

Multiple Inheritance

  • 1,463