TypeScript父类调用路由初始化方法时子类回调函数为undefined
问题原因
该问题由JavaScript/TypeScript的类实例初始化顺序导致,具体执行逻辑如下:
- 实例化子类时,首先执行
super()进入基类构造函数,此时只会初始化基类定义的实例属性,子类的实例属性还未开始初始化 - 你的代码中
getAllUsers、createAPost是用箭头函数语法定义的实例属性,并非挂载在原型链上的类方法,这类属性的初始化会在基类构造执行完成、回到子类构造逻辑后才会执行 - 因此基类构造中调用
initializeRoutes()时,两个路由处理函数还未被赋值,自然为undefined;等基类构造跑完、子类实例属性初始化完成后,子类构造中再调用initializeRoutes()就能正常获取到函数
解决方案
以下是3种可直接落地的改造方案:
方案1(改动最小):删除基类构造中的initializeRoutes调用
仅保留子类构造中super()执行完成后的调用即可,此时子类实例属性已经初始化完成:
// 基类Controller改造后代码 import express, {Router} from "express"; export abstract class Controller { public router = express.Router(); public path: string; protected constructor(path: string) { this.path = path; // 移除原本的this.initializeRoutes()调用即可 } abstract initializeRoutes(): void; }
方案2:将路由处理函数改为原型方法,注册时绑定this
原型方法在类定义完成时就已挂载到原型链,基类构造执行期间也能正常访问,不会出现undefined问题:
// 子类UserController改造后核心代码 export class UserController extends Controller { // ...其余代码不变 // 改为普通类方法(原型方法) 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(): void { // 注册时绑定this,避免回调执行时上下文丢失 this.router.get(this.path, this.getAllUsers.bind(this)); this.router.post(this.path, this.createAPost.bind(this)); } }
该方案可以保留基类构造中initializeRoutes()的调用逻辑,不需要子类额外处理。
方案3:工厂模式统一初始化
如果需要收拢初始化逻辑,避免子类漏写调用,可以用工厂函数统一创建Controller实例:
// 工厂函数定义 export function createController<T extends Controller>(ControllerCtor: new (path: string) => T, path: string): T { const instance = new ControllerCtor(path); instance.initializeRoutes(); return instance; } // 使用示例 const userController = createController(UserController, '/users');
内容的提问来源于stack exchange,提问作者Błażej Kubiński
相关产品推荐
相关产品推荐

