lundi 1 octobre 2018

Advanced JavaScript Inheritance in TypeScript

Advanced JavaScript Inheritance in TypeScript

One of the wonderful aspects of JavaScript is the number of different ways objects can inherit encapsulation from others. However, TypeScript puts heavy constraints on the possibilities for writing a module, from this perspective.

In JavaScript, you have the option to achieve Multiple Inheritance -- rather, Mixin functionality -- through the use of Constructor Hijacking, which is an extremely powerful feature of the language:

var Base = function Base() {

    function f() {
        console.log('datum: %s', this.datum);
    }

    function method() {
        this.a();
        this.b();
        this.c();
    }

    // export precepts
    this.datum = true;
    this.f = f;
    this.method = method;

    return this;
};

var A = function A() {

    function a() {
        this.f();
    }

    // export precepts
    this.a = a;

    return this;
};
var B = function B() {

    function b() {
        this.f();
    }

    // export precepts
    this.b = b;

    return this;
};
var C = function C() {

    function c() {
        this.f();
    }

    // export precepts
    this.c = c;

    return this;
};

var Klass = function Klass() {
    var config = { };

    function init() {
        this.method();
    }

    // export precepts
    Base.call(this);
    A.call(this);
    B.call(this);
    C.call(this);
    this.config = config;
    this.init = init;

    return this;
};

var klass = new Klass();
klass.init();
// > datum: true
// > datum: true
// > datum: true

This allows a developer to break their code out in discrete modules that only need follow a pattern or convention in order to extend another module, keeping the Single Responsibility Principle and the Open-Close Principle of SOLID pristine.

Analysis

The code above should log the string datum: true 3 times. This is because Klass decorates (or mixes-in) the Base class so that classes A-C do not throw a Run-Time Error as they invoke this.f. The CallStack of the process looks something like the following:

  • [Klass] init
  • [Base] method
  • [A] a
  • [Base] f
  • [Console] log
  • [B] b
  • [Base] f
  • [Console] log
  • [C] c
  • [Base] f
  • [Console] log

Notes

The CallStack is fairly arbitrary and trivial. Also, this code could be seen as non-SOLID, but just assume we're using The Template-Method Pattern if it helps to get around the crudeness of the example.

Also, there will undoubtedly be a soul to pipe up and say something about how everything we know about the example above violates TypeScript. Bear in mind, "TypeScript is a superset of JavaScript" is simply, plainly, and blatantly wrong -- I won't even argue why, you should already know this if you are using TypeScript.

Question:

Given the code above, how can one achieve such functionality using valid TypeScript syntax? I am also open to leveraging Design Patterns if necessary, though this is still not ideal.

Aucun commentaire:

Enregistrer un commentaire