question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Contructor injection does noot work with classes

See original GitHub issue

Description

I have AuthRouter class depends on AuthController which is also depends on AuthService. I inject the dependencies in the constructor of each class but when I call the this.authService inside the AuthController, the injection does not work.

Minimal code-snippet showcasing the problem AuthRouter:




I create a class AuthRouter:

import { Router } from 'express'
import Container, { Service } from 'typedi'
import AuthenticationController from './index'

@Service()  
class AuthenticationRouter {
  constructor (private readonly authenticationController: AuthenticationController) {}

  getRouter () {
    const router = Router()
    router.get('/auth/url', this.authenticationController.getAuthUrl)

    return router
  }
}

const authRouter = Container.get(AuthenticationRouter)
const routes = authRouter.getRouter()

export default routes

AuthController:

import { Request, Response } from 'express'
import { Service } from 'typedi'
import AuthenticationService from './authentication-service'

@Service()
class AuthenticationController {
  constructor (private readonly authService: AuthenticationService) {}

  async getAuthUrl (req: Request<{}, {}, {}, {redirect: string}>, res: Response) {
    return res.redirect(this.authService.generateAuthenticationUrl(req.query.redirect))
  }

  
}

export default AuthenticationController

Expected behavior

The dependency should be injected correctly by constructor.

Actual behavior

Dependency does not injected and I got an error:


(node:6064) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'authService' of undefined

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:8

github_iconTop GitHub Comments

2reactions
JoaoLSScommented, Sep 17, 2021

this has nothing to do with typedi, is about javascript binding, see the error:

Cannot read property 'authService' of undefined

this means that this is undefined, and that’s because when you pass the getAuthUrl function to router.get it’s this is not the AuthenticationController instance anymore. test this code to see what happens:

//this class is like the AuthenticationController one
class SomeClass {
    constructor(private readonly someProperty = { someInnerProperty: 'someString' }) {}

    someMethod() {
        console.log(this.someProperty.someInnerProperty)
    }
}

//this class is like the AuthenticationRouter one
class SomeOtherClass {
    constructor(private readonly someClass = new SomeClass()) {}

    someOtherMethod() {
       // im passing a function of the instance "someClass" to setTimeout the same way you're passing a function of the 
       // instance "authenticationController" to router.get
        setTimeout(this.someClass.someMethod, 100)
    }
}

const instance = new SomeOtherClass()
// it will break the exact same way
instance.someOtherMethod()

/***********************************/

//suppose a pseudo implementation of router.get:

router.get(url: string, callback: RequestHandler) {

        // do something

       // here callback doesnt belong to any class, it is just a detached function, and its `this` variable will be the `this` on the
       // scope of router.get  
       callback(req,res,next)
}

//it's like js is doing something like that: 

const callback = this.authenticationController.getAuthUrl
router.get('/auth/url', callback)

// it copies the reference to the function but it doesn't copy the scope where the function is declared
// so to capture the this argument on the function you can do one of two things

//1. use bind() -> this method is available in any function, what it does is that it attaches the value you pass it to the this argument of the function

@Service()
class AuthenticationController {
  constructor (private readonly authService: AuthenticationService) {
     // here im saying that independent of where this function is called the `this` argument should always be this `this`, i.e., the
    // the instance that is been initialized
     this.getAuthUrl = this.getAuthUrl.bind(this) 
  }

  async getAuthUrl (req: Request<{}, {}, {}, {redirect: string}>, res: Response) {
    return res.redirect(this.authService.generateAuthenticationUrl(req.query.redirect))
  }

  
}

//2. use arrow functions -> arrow functions uses closures to keep their scope, so they can mantain the `this` argument independent of where they are called

@Service()
class AuthenticationController {
  constructor (private readonly authService: AuthenticationService) {}

 // here the arrow function is trapping the this of its scope, i.e. the instance of the class where it is defined
 getAuthUrl = async (req: Request<{}, {}, {}, {redirect: string}>, res: Response) => {
    return res.redirect(this.authService.generateAuthenticationUrl(req.query.redirect))
  }

  
}

0reactions
github-actions[bot]commented, Oct 18, 2021

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Dependency injection in a class not working on a worker service
"The issue I am facing is where I say : var partService = new PartService(); It is telling me that I am missing...
Read more >
3.4.1.1 Constructor-based dependency injection - Spring
Dependency injection (DI) is a process whereby objects define their dependencies, ... However, most Spring users do not work with these classes directly ......
Read more >
Constructor Dependency Injection in Spring - Baeldung
This quick tutorial will explore a specific type of DI technique within Spring called Constructor-Based Dependency Injection, which simply put, ...
Read more >
Why You Should Use Constructor Injection in Spring
Dependency injection is a common approach to implement loose coupling among the classes in an application. There are different ways of ...
Read more >
Contexts and Dependency Injection - Quarkus
Quarkus DI solution (also called ArC) is based on the Contexts and Dependency Injection for Java 2.0 specification. However, it is not a...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found