3

While playing around with the online TypeScript compiler, I've noticed that the creators chose to implement inheritance with the help of the following method (which I have made somewhat more readable):

var __extends = function(type, base) {
    for (var prop in base) { // why copy the properties?
        if (base.hasOwnProperty(prop)) {
            type[prop] = base[prop];
        }
    }
    function __() { // <- ?
        this.constructor = type;
    }
    __.prototype = base.prototype.
    type.prototype = new __();
};

The method is then used in the following manner:

var Item = (function () {
    function Item() {
    }
    return Item;
})();

var Sword = (function (base) {
    __extends (Sword, base);
    function Sword() {
        base.call(this);
    }
    return Sword;
})(Item);

I have a hard time understandind the implementation of the __extends method.
What is the purpose of manually copying all the properties from the extending type? Won't inheriting the prototype take care of that?

Is there a performance penalty when searching for properties in the prototype tree as opposed to directly copied properties?

The second part I also can't understand the purpose of. Instead of doing this:

function __() {
    this.constructor = type;
}
__.prototype = base.prototype.
type.prototype = new __();

Couldn't this simply be implemented as:

type.prototype = base.prototype;
Geek Num 88
  • 5,264
  • 2
  • 22
  • 35
Acidic
  • 6,154
  • 12
  • 46
  • 80

2 Answers2

3

Extends copies prototype and static members (shallow copy). Statics are Parent.things and prototypes are Parent.prototype.things The way the prototype.things are copied is how the poly fill for Object create works.

var __extends = this.__extends || function (d, b) {
    //Copies Item.things to Sword.things
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    //Copies Item.prototype.things to Sword.prototype.things
    //this is a polyfill for Object.create
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var Sword = (function (_super) {
    __extends(Sword, _super);
    function Sword() {
        _super.apply(this, arguments);
    }
    return Sword;
})(Iten);

Consider the following Typescript:

class Item {
    public static DAMAGE = 0;
};

In JavaScript this will be:

var Item = (function () {
    function Item() {
    }
    Item.DAMAGE = 0;
    return Item;
})();

How would I use static DAMAGE? See the following example (simplefied JS);

//Using static to make general getters and setters
var Item = function(damage){ 
  this._data = [];
  this.set(Item.DAMAGE,damage)
};
Item.DAMAGE=0;
Item.prototype.get=function(dataIndex){
  return this._data[dataIndex];
};
Item.prototype.set=function(dataIndex,val){
  //maybe execute Item.rules[dataIndex].set(val) for validating value
  this._data[dataIndex] = val;
  return val;
};
var i = new Item(22);
//I could get damage using i.get(0)
//but the following reads more easily
i.get(Itesm.DAMAGE);

Introduction about constructor functions, prototype, inheritance, mix ins and the this value here: https://stackoverflow.com/a/16063711/1641941

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • As far as I can tell the missing things have no impact on my example, so that's why i removed them. – Acidic Nov 18 '13 at 01:24
  • @Acidic Correct, I missed something. Created an example of how to use static and Rayan's example shows a good one for factory patterns. – HMR Nov 18 '13 at 01:40
3

Great question. Let's break this down.

var __extends = function(type, base) {
    // ** Begin static-side inheritance **
    for (var prop in base) {
        if (base.hasOwnProperty(prop)) {
            type[prop] = base[prop];
        }
    }
    // ** End static-side inheritance **

    // ** Begin Object.create polyfill **
    function __() { // <- ?
        this.constructor = type;
    }
    __.prototype = base.prototype.
    // ** End Object.create polyfill **

    // ** Prototype setup **
    type.prototype = new __();
};

First up is the static-side inheritance. TypeScript's type system enforces that if some type has a static method, a derived class also has that method (or a method whose signature is a subtype of the base class method). This is useful for things like factory patterns, e.g.:

class Car {
    static create(name: string, topSpeed: number): Car {
        // ...
        return new this(/* ... */);
    }
}

class RaceCar extends Car {
    // RaceCar ignores topSpeed because it goes super fast
    static create(name: string): RaceCar {
        // ...
        return new this(/* ... */);
    }   
}

class Truck extends Car {
}

function makeTestCar(t: typeof Car) {
    return t.create('test', 60);
}
var car = makeTestCar(Car); // returns an instance of Car
var raceCar = makeTestCar(RaceCar); // returns an instance of RaceCar
var truck = makeTestCar(Truck); // OK, even though Truck doesn't explicitly implement 'create'

Note that in the last line, the Truck class got its create method via its base class Car. The copying is needed because Truck.__proto__ is not Car.

Next up we have the Object.create polyfill. It's not sufficient to write type.prototype = base.prototype because the __proto__ (AKA [[proto]]) and prototype members are not the same thing. Other answers on SO have given a better rundown of prototypal inheritance so I'll defer to those.

Finally the last line sets up the prototype of the constructor so that the created object has the correct __proto__

Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • Looking at the answers to this question in retrospect and now that I am more experienced with TS, I feel that your answer is more complete and informative, and provides more insight. Changed to this being the correct answer. – Acidic Jan 18 '15 at 07:59