0

Im building a simple rest api in typescript+expressjs. I wanted to build it with classes and i'm stuck on the part with routing classes. My idea was to build a base class (baseRouter) that could be extended by specific routers (IndexRouter). But trying to do this, i end up registering routes from baseRouter and not the IndexRouter. What am i doing wrong?

export class baseRouter {
    protected static _inst: baseRouter;
    protected _router: express.Router;
    protected _routeBase: string = "/base";

    protected constructor() { }

    public static route(router: express.Router) {
        if (typeof this._inst === 'undefined') this._inst = new this();
        this._inst._router = router;
        this._inst._registerRoutes();
    }

    protected _registerRoutes() {
        this._router.get(this._routeBase, this._doGet);
        console.log('Calling base route');
    }

    protected _doGet(req: express.Request, res: express.Response) {
        res.status(200);
        res.send('isBase');
    }
}

export class indexRouter extends baseRouter {
    protected _routeBase = "/index";

    protected _registerRoutes() {
        this._router.get(this._routeBase, this._doGet);
        console.log('Calling index route');
    }

    protected _doGet(req: express.Request, res: express.Response) {
        res.status(200);
        res.send('isIndex');
    }

    protected _doPost(req: express.Request, res: express.Response) {
        res.status(200);
        res.send('isIndexPost');
    }
}

Entry method to both classes is .route(router). Ideally my solution to my understanding was going to be like:

BaseRouter.route() -> calls baseRouter._registerRoutes() -> ...
IndexRouter.route() -> calls indexRouter._registerRoutes() -> ...

Is this achievable without redeclaring .route() function every time?

4
  • I'm unable to reproduce your error. When calling BaseRouter.route() it calls BaseRouter._registerRoutes and when calling IndexRouter.route() it calls IndexRouter._registerRoutes (though there's no need to have this method twice) Commented Feb 13, 2017 at 8:26
  • Odd, i updated the code with console.log's and it clearly displays to me the "calling base route" twice in console while running node. Commented Feb 13, 2017 at 11:03
  • It runs fine for me on node as well. What version of typescript are you using? Commented Feb 13, 2017 at 11:08
  • Typescript - 2.1.6 Node - 6.9.5 Commented Feb 13, 2017 at 11:23

1 Answer 1

1

The problem is with this line:

if (typeof this._inst === 'undefined') this._inst = new this();

When you first call BaseRouter.route() this line sets the BaseRouter._inst static member.
But because IndexRouter extends BaseRouter it also inherits the static members, so IndexRouter._inst also exists and so in that line an instance of IndexRouter won't be created.

The reason that it runs well in playground is that it targets es5, and you're probably targeting es6.
The difference is that when targeting es6 the classes will be converted into es6 classes where the static behavior is different.

To fix this you can do:

public static route() {
    if (typeof this._inst === 'undefined' || this._inst.constructor !== this) this._inst = new this();
    this._inst._registerRoutes();
}

Edit

As far as I'm aware there's no trick for having a "static polymorphic this member`, for example this fails:

class BaseRouter<T extends BaseRouter<any>> {
    protected static _inst: T;
    ...
}

Because:

Static members cannot reference class type parameters

So I'm pretty sure that you'll have to stick with: static _inst: baseRouter.

You can try redeclaring the member in IndexRouter:

class IndexRouter extends BaseRouter {
    protected static _inst: IndexRouter;
    ...
}

You can also have a generic getter:

class BaseRouter {
    private static _inst: BaseRouter;
    protected static _getInstance<T extends BaseRouter>(): T {
        return this._inst as T;
    }
    ...
}
Sign up to request clarification or add additional context in comments.

4 Comments

Yes, im targetting es6, since node is (or at least should) support most of its features. Thanks for explaination and your code works perfectly.
One more question, im going to keep it here, cause its somehow related. Since _inst is an inherited instance, that should be a reference to the "current" class object, how do i set the type of _inst to the right class? Right now its the "baseRouter" in all of the subclasses (easy to test - you dont get protected/private methods completion)
Unfortunately that's not supported yet. There's an issue for it: Polymorphic "this" for static members.
Thanks, this solves my problem :) Shame i cant upvote more than once.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.