【问题标题】:Injected function into a class constructor throws undefined将函数注入类构造函数会引发未定义
【发布时间】:2020-12-09 23:50:51
【问题描述】:

我正在学习使用 typescript 来构建 API,我现在遇到了两个问题。首先,我有一个有点通用的 PostController 类,它可以接受实现 PostMethod 接口的用例,例如

export interface PostMethod {
  add: (req: Request, res: Response) => Promise<any> // not sure if it should be returning any
}

就是接口,通用控制器长这样。

export class PostController implements PostMethod {
  constructor(public postMethod: any) {}

  async add(req: Request, res: Response) {
    let { ...incomingHttpBody } = req.body
    console.log('body', incomingHttpBody)
    console.log(this.postMethod)
    type Source = {
      ip: string
      browser: string | string[] | undefined
      referrer: string | string[]
    }
    let source = {} as Source
    source.ip = req.ip
    source.browser = req.headers['User-Agent']
    if (req.headers.Referer) {
      source.referrer = req.headers.Referer
    }
    const newItem = await this.postMethod({ source, ...incomingHttpBody })
    return apiResponse({
      status: true,
      statusCode: 201,
      message: 'Resource created successfully',
      data: [newItem]
    })
  }
}

然后,我可以像这样使用PostController

...
const postMethod = new AddUser(UsersDb).addUser
export const postUser = new PostController(postMethod)
...

AddUser 类看起来像这样,

export class AddUser {
  constructor(public usersDb: UserDatabase) {}

  async addUser(userInfo: IUser) {
    console.log({...userInfo})
    const exists = await this.usersDb.findByEmail(userInfo.email)
    if (exists) {
      throw new UniqueConstraintError('Email address')
    }
    const user = new UserFactory(userInfo)
    user.makeUser()
    const { email, ...details } = user.user
    const newUser = await this.usersDb.insert({ email, ...details })
    const id = newUser.user._id
    await createWallet(id)
    // await publisher(id.toString(), 'newuser.verify')
    // await consumer('verify_queue', verifyUser, '*.verify')
    return newUser
  }
}

当我执行 req.body 的 console.log 时,我得到了传入的 body,但我不断得到 TypeError: Cannot read property 'postMethod' of undefined。我也不确定如何注释构造函数。我不知道我做错了什么,当我 console.log postUser 时,我确实看到函数作为参数传递到控制台,但是当我尝试发送请求时,它失败了。

请帮忙,谢谢。

【问题讨论】:

    标签: javascript node.js typescript express mongoose


    【解决方案1】:

    我认为这里有一些误解。

    您正在定义一个接口PostMethod,它本身不是一个方法。它是一个接口。因此,如果您尝试传递 this 的实例,请不要只传递 FUNCTION(或方法)。但是传递一个PostMethod 的实例。

    它可能看起来像这样(看看我所做的更改):

    export interface PostMethod {
      add: (req: Request, res: Response) => Promise<any> // not sure if it should be returning any
    }
    
    export class PostController implements PostMethod {
      constructor(public postMethod: any) {}
    
      async add(req: Request, res: Response) {
        // cloning? if so, do it like this
        let incomingHttpBody = { ...req.body }
        console.log('body', incomingHttpBody)
        console.log(this.postMethod)
        type Source = {
          ip: string
          browser: string | string[] | undefined
          referrer: string | string[]
        }
        let source = {} as Source
        source.ip = req.ip
        source.browser = req.headers['User-Agent']
        if (req.headers.Referer) {
          source.referrer = req.headers.Referer
        }
    
        // change: I made a change here, you have to call postMethod.add!
        const newItem = await this.postMethod.add({ source, ...incomingHttpBody })
        return apiResponse({
          status: true,
          statusCode: 201,
          message: 'Resource created successfully',
          data: [newItem]
        })
      }
    }
    
    // change: instantiate AddUser which in turn implements PostMethod that can be passed...
    const postMethod = new AddUser(UsersDb)
    export const postUser = new PostController(postMethod)
    
    // change: implementing PostMethod!
    export class AddUser implements PostMethod {
      constructor(public usersDb: UserDatabase) {}
      
      // change: new method! this is a must, because we're implementing PostMethod!
      async add(req: Request, res: Response) {
        // this gets called, not addUser!
      }
    
      async addUser(userInfo: IUser) {
        const exists = await this.usersDb.findByEmail(userInfo.email)
        if (exists) {
          throw new UniqueConstraintError('Email address')
        }
        const user = new UserFactory(userInfo)
        user.makeUser()
        const { email, ...details } = user.user
        const newUser = await this.usersDb.insert({ email, ...details })
        const id = newUser.user._id
        await createWallet(id)
        // await publisher(id.toString(), 'newuser.verify')
        // await consumer('verify_queue', verifyUser, '*.verify')
        return newUser
      }
    }
    

    【讨论】:

    • 我希望这能让你前进;)
    • 感谢您抽出宝贵时间提供帮助,我知道您在那里做了什么,但我的目标是遵循清洁代码架构,因此为什么在我的 ``` 类 AddUser``` 中我没有将req, res 直接添加到它,相反,我制作了class AddUser 的用例,然后将其传递给PostController Class. 以保持关注点分离。有没有机会,您可以向我指出实现这种架构的方法吗?非常感谢:-)
    • 我建议,如果它有助于解决您描述的问题,您将一个答案标记为正确答案。我认为您的目标是使用干净的代码并使其变得更好不是这里的主题。我会说你为此打开了另一个问题,并用“干净的代码”等标记了那个问题。
    • 当然我完全理解你的目标,但我个人首先解决了我的编译问题,然后我尝试清理我的代码。这是两个独立的任务。你知道我的意思吗?
    • 你是对的,这实际上是有道理的。先解决问题,再清理代码。
    【解决方案2】:

    您需要先实例化该类,然后才能使用该方法...

    ...
    const postMethod = new AddUser(UsersDb)
    
    //then use it
    
    postMethod.addUser()
    export const postUser = new PostController(postMethod)
    
    ...
    

    但是您可以将其设为静态,因此您不需要实例化它:

    static async add(req: Request, res: Response) { ....
    

    接口中的函数通常是这样写的:

    export interface PostMethod {
      constructor(): void,
      add(req: Request, res: Response): Promise<any>
    }
    

    【讨论】:

    • 这不是 100% 正确的。如果您只查看方法,“但是您可以将其设为静态,因此您不需要实例化它”是正确的。这不会改变问卷错误地传递方法的事实。我会回答的。
    【解决方案3】:

    我解决了这个问题,经过大量挖掘和阅读后,我意识到这是this 失去其上下文的问题。因此,当我做这样的事情时,为什么我会变得不确定。

    const postMethod = new PostController(usecase)
    

    解决方法是像这样在构造函数中绑定 this,

    this.add = this.add.bind(this)
    

    解决了这个问题,因为当类被实例化时,它不仅指向类的方法,还指向类的关联。

    【讨论】:

      【解决方案4】:

      传递给 PostMethod 构造函数的 postMethod 参数没有存储在任何地方。将其存储在私有类变量中,然后使用私有类变量。

      喜欢-

      const _postMethod = postMethod;
      

      然后一边打电话一边打电话

      const newItem = await this._postMethod({ source, ...incomingHttpBody })
      

      【讨论】:

      • 原代码有很多错误,这不是正确的解决方案。
      猜你喜欢
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      • 2012-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多