Roger Alsing Roger Alsing - 1 month ago 24
TypeScript Question

TypeScript applying aspects to classes

Is there any way to hook into the declaration process of classes in TypeScript?

e.g. consider the following class

class Foo extends Bar
{
constructor() { super(); }
}


I currently apply aspects to this type by calling my weaver manually like so:

weaver.applyAspects(Foo);


Meaning that I modify the behavior of some of the methods on the class.
I would rather see that this happens automagically just by inheriting in the above case the "Bar" super class.

Doing some funky stuff in the constructor seems wrong since that would apply to every new instance, not just the prototype itself.

I guess I could overwrite the __extends function (which is used in the generated javascript output) , but that seems a bit funky too.

Any other alternatives?

Answer

If you want to use AOP in TypeScript, you can use an existing AOP framework by supplying a definition for it. Here is a full example using jQuery.aop.

Using AOP in this manner won't affect any of your existing definitions as the AOP code is invisible on the code you are cross-cutting.

aop.d.ts

declare class Advice {
    unweave(): void;
}

declare interface PointCut {
    target: Object;
    method: string;
}

declare class Aop {
    before: (pointCut: PointCut, advice: Function) => Advice[];
    after: (pointCut: PointCut, advice: Function) => Advice[];
    afterThrow: (pointCut: PointCut, advice: Function) => Advice[];
    afterFinally: (pointCut: PointCut, advice: Function) => Advice[];
    around: (pointCut: PointCut, advice: Function) => Advice[];
    introduction: (pointCut: PointCut, advice: Function) => Advice[];
}

interface JQueryStatic {
    aop: Aop;
}

app.ts

/// <reference path="jquery.d.ts" />
/// <reference path="aop.d.ts" />

class ExampleClass {
    exampleMethod() {
        alert('Hello');
    }
}

jQuery.aop.before(
    { target: ExampleClass, method: 'exampleMethod' },
    function () { alert('Before exampleMethod'); }
);

jQuery.aop.after(
    { target: ExampleClass, method: 'exampleMethod' },
    function () { alert('After exampleMethod'); }
);

var example = new ExampleClass();
example.exampleMethod();

Example source: AOP with TypeScript.

Update

To add the same concerns to a class and all compatible classes, you can't re-use the base class points. This is because the wrapper is the original base-class function wrapped in the points, which will be the wrong implementation for the class that extends the base class.

The only time this would be valid is if your function only called super() in which case it would work anyway.

Here is how I would add the same concerns to many classes - the AopHelper would only need to exist once in your program:

/// <reference path="jquery.d.ts" />
/// <reference path="aop.d.ts" />

class ExampleClass {
    exampleMethod() {
        alert('Hello');
    }
}

class SecondClass extends ExampleClass {
    exampleMethod() {
        alert('World');
    }
}

class AopHelper {
    static weave(targets: Object[], method: string, point: Function, advice: Function) {
        for (var i = 0; i < targets.length; i++) {
            point({ target: targets[i], method: method }, advice );
        }
    }
}

var classes: any[] = [ExampleClass, SecondClass];

AopHelper.weave(classes, 'exampleMethod', jQuery.aop.before, () => { alert('Before exampleMethod'); });
AopHelper.weave(classes, 'exampleMethod', jQuery.aop.after, () => { alert('After exampleMethod'); });

var example = new SecondClass();
example.exampleMethod();
Comments