0

My application has an api wrapper class originally created by typing it out in TypeScript and copying/pasting the javascript into my app.

So the class def looks like this:

var SiteApi = (function () {
  function SiteApi(initially) 
  {
    //stuff
  }

  SiteApi.prototype.method1 = function(){/*stuff*/};
  SiteApi.prototype.method2 = function(){/*stuff*/};

  return SiteApi;
})();

Now when they are on the admin page, I want to add an additional admin.js file that will contain admin methods. For example

SiteApi.prototype.Admin.method1 = function(){/*stuff*/};

I found an example that does the "end result" that I want:

// file main
function SomeObject() {
    for (var i = 0, ii = SomeObject.Partial.length; i < ii; i++) {
         SomeObject.Partial[i].apply(this, arguments);
    }
}

SomeObject.Partial.SomeName = function() {
   ...
}

// file extra
SomeObject.Partial.SomeOtherName = function() {
   ...
}

(from: Is it possible to give javascript partial class behavior like C# or monkey patching like Ruby does?)

However, the type of class definition they are using is different.

How can I keep the TypeScript style class definition and yet do something similar to this example to add on the admin functions?

For reference, we use our class like so:

siteApi = new SiteApi();

So I imagine there will also need to be a line of code tying the admin functions into it.

Note, I'm Ok with using something like SiteApi.admin_method1 but the issue is that with TypeScript style classes the prototypes are defined in the definition and the object is executed, so it doesn't seem straightforward how to add in more prototypes later.

Community
  • 1
  • 1
AwokeKnowing
  • 7,728
  • 9
  • 36
  • 47
  • I see a vanilla constructor written in an _IIFE_. Is this what you're referring to when you say "TypeScript-style"? Also, is _Admin_ meant to introduce more to the prototype or just do more construction? – Paul S. Oct 27 '15 at 21:48
  • @PaulS. I've heard the TypeScript class style is kind of a mix of 2 different patterns, since it puts the prototypes in the middle, has a contructor named like the class, and "executes" the definition at the time of definition. If this is IIFE, then yes. Perhaps TypeScript complies to several forms depending on usage. But the above is exactly what we have, and it's TypeScript origin is irrelevant. – AwokeKnowing Oct 27 '15 at 21:50

1 Answers1

4

Option 1

Extend SiteApi but shadow the original, e.g. your admin.js loaded after would contain something like

SiteApi = (function (old_SiteApi) {
    function SiteApi() {
        old_SiteApi.apply(this, arguments);
        // further construction
        this.admin_method1 = function () {/* some admin instance method */};
    }
    SiteApi.prototype = Object.create(old_SiteApi.prototype);
    // add more prototype things
    SiteApi.prototype.admin_method2 = function () {/* admin stuff through prototype */},
    return SiteApi;
}(SiteApi));

Option 2

Let SiteApi know to expect more stuff later, e.g. the original definition would become

var SiteApi = (function () {
    function SiteApi(initially) {
        //stuff
        var i;
        for (i = 0; i < SiteApi.Extras.length; ++i) {
            SiteApi.Extras[i].apply(this);
        }
    }
    SiteApi.Extras = [];

    SiteApi.prototype.method1 = function () {/* stuff */};
    SiteApi.prototype.method2 = function () {/* stuff */};

    return SiteApi;
}());

Then the admin.js would do

SiteApi.Extras.push(
    function () {
        // stuff to make `this` into an Admin object
        this.admin_method1 = function () {/* some admin instance method */};
    }
);

Option 3

The prototype of SiteApi is still exposed, if you don't need to do more construction and just need new methods you could simply add them in admin.js

Object.assign(SiteApi.prototype, {
    admin_method1: function () {/* admin stuff through prototype */},
    admin_method2: function () {/* more admin stuff through prototype */}
});

This last option is the only one which will effect instances of SiteApi created before admin.js was loaded.

Of course, you could mix options 2 and 3 together as well. Similarly, you could go with option 1 but call the new constructor a different name rather than shadowing the original, like AdminApi, and use that instead of SiteApi on the admin page.

Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • If the Admin functions don't need construction, and the siteapi.js and admin.js are loaded asyncronously (and siteapi contrstruction does not depend on admin functions), what would be the best way to set up admin.js, so it will work even it it gets loaded first? :) I can guarantee that api=new SiteApi() will not be called until both javascripts are loaded. And by "work" I don't mean you can call admin functions before siteapi.js is loaded, but that they will end up in the SiteApi prototype – AwokeKnowing Oct 27 '15 at 23:02
  • The code in _admin.js_ must be invoked after _siteapi.js_ if you want it to build upon _SiteApi_ – Paul S. Oct 27 '15 at 23:11
  • I know, I was just wondering if there was a better way than setTimeout checking if the other one is there yet. – AwokeKnowing Oct 28 '15 at 04:10