I'm currently working on a class I am using as middleware in express. Before i go into the class keep in mind that i will be injecting the middleware later by first creating an instance of my Class "Authenticator" and then injecting it's method with
app.use(authInst.express)
So the key point will be the execution context (this) of this function. This is the code I've got so far
Alternative 1
class Authenticator {
opts:IAuthOpts ;
express: RequestHandler| ErrorRequestHandler
constructor(opts:IAuthOpts){
this.opts = opts;
this.express = function(req:Request, res:Response, next:NextFunction){
if(this.opts.enabled) {
mainController(req, res, next, this.opts)
} else {
next();
}
}
}
}
This is working. Yet I don't want to write the function in the constructor as I find it pretty ugly code. Putting the express method directly in the class like this
Not working
class Authenticator {
opts:IAuthOpts;
constructor(opts:IAuthOpts){
this.opts = opts;
}
express(req, res, next){
if(this.opts.enabled) {
mainController(req, res, next, this.opts)
} else {
next();
}
}
}
will not work as the execution context from express will give me this as undefined. So what's left is using this kind of alternative
Alternative 2
class Authenticator {
opts:IAuthOpts ;
express: RequestHandler| ErrorRequestHandler
constructor(opts:IAuthOpts){
this.opts = opts;
this.express = _express.bind(this);
}
private _express(req, res, next){
if(this.opts.enabled) {
mainController(req, res, next, this.opts)
} else {
next();
}
}
}
Now this works too and is my preferred solution for this problem so far, as it would also be easy to outsource the method to another file and keeping my files small. The downside is the bind. I am not a big fan of bind as I want my functions to return the same value if i invoke them with the same parameters no matter where they are called from and in this case you always need to bind the class to it.
Is there a better solution to outsource a method from a TypeScript class and still not having to inject the execution context with bind?
In place of the bind, you can use an arrow function:
class Authenticator {
opts:IAuthOpts ;
constructor(opts:IAuthOpts){
this.opts = opts;
}
express = (req, res, next) => {
if(this.opts.enabled) {
mainController(req, res, next, this.opts)
} else {
next();
}
}
}
If you then wanted to move the implementation to another file, it would probably be clearest to define a function that takes the Authenticator
as an ordinary parameter along with req
, res
, and next
and then call that function from your arrow function:
class Authenticator {
opts:IAuthOpts ;
constructor(opts:IAuthOpts){
this.opts = opts;
}
express = (req, res, next) => otherFunction(this, req, res, next);
}
// In other file
function otherFunction(authenticator: Authenticator, req: Request, res: Response, next: NextFunction) {
if(authenticator.opts.enabled) {
mainController(req, res, next, authenticator.opts)
} else {
next();
}
}
If this wasn't what you were looking for, please clarify the question.