【问题标题】:Typescript - callback functions get undefined when used from superclassTypescript - 从超类使用时回调函数未定义
【发布时间】:2021-10-13 18:17:14
【问题描述】:

我对 typescript 和 node.js 比较陌生,但我想学习一些东西并尝试一些新的东西,包括 angular、node 和 express。我仍然尝试找到一些好的做法来将 express 项目拆分为几个较小的部分,但不知何故我遇到了问题。我想创建一个抽象的 Controller 类和一些子类,它们将是由 express.Router() 支持的典型 REST 控制器。

这是基类 - 控制器:

import express, {Router} from "express";

export abstract class Controller {
  public router = express.Router();
  public path: string;

  protected constructor(path: string) {
    this.path = path;
    console.log('Calling initializeRoutes() from superclass');
    this.initializeRoutes();
  }
  abstract initializeRoutes(): void;
}

还有子类 - 这只是一个例子 - UserController 类:

import {Controller} from "./Controller";
import express from "express";

export class UserController extends Controller {

  private users = [
    {name: 'User1', password: 'password1'},
    {name: 'User2', password: 'password2'},
    {name: 'User3', password: 'password3'}
  ]

  constructor(path: string) {
    super(path);
    console.log('Calling initializeRoutes() from subclass');
    this.initializeRoutes();
  }

  initializeRoutes(): void {
    // console.log(this.router);
    console.log(this.getAllUsers);
    console.log(this.createAPost);
    // this.router.get(this.path, this.getAllUsers);
    // this.router.post(this.path, this.createAPost);
  }

  getAllUsers = (request: express.Request, response: express.Response) => {
    response.send(this.users);
  }

  createAPost = (request: express.Request, response: express.Response) => {
    const user = request.body;
    this.users.push(user);
    response.send(user);
  }

主要问题是我想在基类中调用initializeRoutes(),但是当我这样做时会抛出错误,即我不能在路由器中使用未定义的回调。我做了一些调试,发现当在基类中使用 initializeRoutes() 时,来自子类的回调是未定义的,而从子类运行时一切正常:

[1] [nodemon] starting `ts-node server\server.ts`
[1] Calling initializeRoutes() from superclass
[1] undefined
[1] undefined
[1] Calling initializeRoutes() from subclass
[1] [Function (anonymous)]
[1] [Function (anonymous)]

这是为什么呢?我做错了什么或错过了什么? 提前致谢

编辑:我尝试了建议的解决方案:

export class UserController extends Controller {

  constructor(path: string) {
    super(path);
    this.getAllUsers = this.getAllUsers.bind(this);
    this.createAPost = this.createAPost.bind(this);
    console.log('Calling initializeRoutes() from subclass');
    // this.initializeRoutes();
  }

  initializeRoutes(): void {
    // console.log(this.router);
    this.getAllUsers = this.getAllUsers.bind(this);
    this.createAPost = this.createAPost.bind(this);
    console.log(this.getAllUsers);
    console.log(this.createAPost);
    this.router.get(this.path, this.getAllUsers);
    this.router.post(this.path, this.createAPost);
  }

}

当我将其放入 initializeRoutes() 时,我得到: [1] TypeError: Cannot read property 'bind' of undefined [1] at UserController.initializeRoutes (C:\Blazej\Projects\pasteur\server\controller\UserController.ts:19:41)

当我将它放入构造函数时,我得到: [1] Error: Route.get() requires a callback function but got a [object Undefined]

【问题讨论】:

    标签: node.js typescript inheritance callback


    【解决方案1】:
    getAllUsers = (request: express.Request, response: express.Response) => {
      response.send(this.users);
    }
    

    这种语法是一个“类字段”,这是一个正在添加到 javascript/typescript 中的功能,但还没有完全实现。 You can see the proposal here

    但是,尽管它仍在通过提案流程,但您现在可以使用(并且正在使用)它,这要感谢转译器。 Typescript 会将您编写的代码转换为最接近的等效代码。例如,这个:

    class Example {
      constructor() {
        super();
      }
    
      someFunction = () => {};
    }
    

    基本上变成了这样:

    class Example {
      constructor() {
        super();
        this.someFunction = () => {};
      }
    }
    

    但请注意,转译后的代码将函数的创建放在了对 super() 的调用之后。必须是这样,但这意味着当 super 运行时,该函数还不存在。

    所以为了让函数提前存在,你需要使用普通的类方法而不是类字段,如下所示:

    getAllUsers(request: express.Request, response: express.Response) {
      response.send(this.users);
    }
    
    createAPost(request: express.Request, response: express.Response) {
      const user = request.body;
      this.users.push(user);
      response.send(user);
    }
    

    当然,您可能故意将它们用作箭头函数,因为您需要 this 的值才能正常工作。因此,我建议绑定功能。如果在初始化期间不需要绑定函数,可以在子类的构造函数中进行:

      constructor(path: string) {
        super(path);
        this.getAllUsers = this.getAllUsers.bind(this);
        this.createAPost = this.createAPost.bind(this);
      }
    

    或者如果你在构建过程中确实需要它们,也许在 initializeRoutes 中进行

      initializeRoutes(): void {
        this.getAllUsers = this.getAllUsers.bind(this);
        this.createAPost = this.createAPost.bind(this);
        this.router.get(this.path, this.getAllUsers);
        this.router.post(this.path, this.createAPost);
      }
    

    【讨论】:

    • 感谢您的快速回答。我不知道它是这样工作的。无论如何,仍然无法正常工作。我尝试了这两种解决方案 - 在构造函数和 initializeRoutes() 中绑定回调,但仍然得到:``` 错误:Route.get() 需要回调函数,但得到了 [object Undefined] ```
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-25
    • 1970-01-01
    • 2012-10-19
    • 2023-04-08
    • 2012-02-23
    • 1970-01-01
    相关资源
    最近更新 更多