1

routes.ts

import { Router } from 'express'
import { MyController } from './Controller/MyController'

const router = Router()

const myController = new MyController()

router.post('/doTheThing', myController.mainFunction)


export { router }

Then the controller

class myController {
  async mainFunction(req: Request, res: Response) {
    const data = req.body
    if (data) {
      const formattedData = formatData(data)
    }
  }  

  async formatData(data) {
    // some code here
  }
}

It gives the following error:

Cannot find name 'formatData' Did you mean the instance member 'this.formatData'?ts(2663)

Ok. It looks simple. Just add 'this' as the error mentions

The problem is if I add this.formatData(data) I get a new error saying:

UnhandledPromiseRejectionWarning: TypeError: Cannot read formatData of undefined

I'm using Insomnia to make the requests.

So what am I doing wrong? This should be something really simple. Calling a function from within the same class.

PlayHardGoPro
  • 2,791
  • 10
  • 51
  • 90
  • You can use the `.bind` method or use an arrow syntax for the `formatData` method. – goto Jul 18 '21 at 20:09
  • Could you give me more info, please? I also tried something with arrow `=>` to call `formatData` but Didn't work neither, unfortunately. – PlayHardGoPro Jul 18 '21 at 20:10
  • Please show us how you are calling `thatClass`. – Bergi Jul 18 '21 at 20:30
  • @Bergi Updated the question with more info – PlayHardGoPro Jul 18 '21 at 20:55
  • 1
    Thanks. Yes, [`this` is `undefined` when the method is used as a callback](https://stackoverflow.com/q/20279484/1048572). I would suggest not using a `class` at all unless you have any actual data properties on your controller. Use a simple object literal. – Bergi Jul 18 '21 at 20:59

1 Answers1

0

You can either use the .bind method that sets the this keyword to the correct value or use an arrow function.

.bind method

class MyController {
  constructor() {
    this.formatData = this.formatData.bind(this);
  }
  async test() {
    this.formatData({ foo: "bar" })
  }
  async formatData(data) { console.log("data", data) }
}

const mc = new MyController()
mc.test()

Arrow functions

By using arrow functions, you eliminate the need to bind the handler.

class MyController {
  async test() {
    this.formatData({ foo: "bar" })
  }
  formatData = async (data) => { console.log("data", data) }
}

const mc = new MyController()
mc.test()

Edit

I would suggest using what Bergi suggested, but if you still insist on using a class and calling its method to handle the route, do the following instead:

const myController = new MyController()

router.post('/doTheThing', (req, res, next) => { 
  myController.mainFunction(req, res, next)
})
goto
  • 4,336
  • 15
  • 20
  • Not sure why but the things stayed the same. `bind` method still telling me I'm trying to read functionName from undefined. With the arrow function the same and gave me error with the `.map` I have inside the second function :S – PlayHardGoPro Jul 18 '21 at 20:27
  • How are you calling these methods? I just added some example calls that work, so you must be doing something different that you're not showing in your original post. – goto Jul 18 '21 at 20:31
  • Actually there isn't much more. The first function is being called from my 'routes.ts` file. And I'm making the request from `Insomnia`. – PlayHardGoPro Jul 18 '21 at 20:40
  • @PlayHardGoPro right, but it's unclear how that method is being called from your `routes.ts` file. If you're saying that what I have above doesn't work for you then your problem is with how you're using that class and/or calling that method. – goto Jul 18 '21 at 20:41
  • @PlayHardGoPro "bind method still telling me I'm trying to read functionName from undefined" - you're probably not creating a new instance of that class, that's why it's complaining about it being `undefined`. – goto Jul 18 '21 at 20:48