TypeScript类私有方法调用报错:this指向为undefined
问题描述
在Node.js + TypeScript + Express构建的项目中,调用AdLibController类的getLibs方法时出现以下错误:
const data = this.transformRequest(req); [1] ^ [1] [1] TypeError: Cannot read properties of undefined (reading 'transformRequest')
访问类内的validator和service属性时也会触发同类错误,将属性/方法改为静态后可正常运行,但需要保留非静态写法完成修复。
相关代码如下:
AdLibController.ts
import { NextFunction, Request, Response } from "express"; import { Controller } from "../../common/Controller"; import { AdLibService } from "./AdLibService"; import { AdLibValidator } from "./AdLibValidator"; import { ErrroHandler } from "../../errors/ErrorHandler"; import { AdLibProps } from "../../ts/types/AdLibProps"; export class AdLibController extends Controller { constructor() { super(); this.service = new AdLibService(); this.validator = new AdLibValidator(); } private transformRequest(req: Request): AdLibProps { const page: number = parseInt((req.query.page as string) ?? 0); const pagination: number = parseInt((req.query.pagination as string) ?? 0); const time = req.query.timestamp; const data: AdLibProps = { timestamp: new Date(time as string), pagination, page, }; return data; } public async getLibs(req: Request, res: Response, next: NextFunction) { const data: AdLibProps = this.transformRequest(req); if (!this.validator.validate(data)) { return next({ status: 400, message: `These properties are not valid: ${this.validator.getInvalidPropertiesAsString()}`, }); } try { const foundAdLibs = await this.service.getLibs( new Date(req.query.timestamp as string), data.page, data.pagination ); return AdLibController.sendResponse(res, foundAdLibs, 200); } catch (e: unknown) { const error = ErrroHandler.ensureError(e); return next({ status: 400, message: error.message, }); } } private service: AdLibService; private validator: AdLibValidator; }
Controller.ts
import { Response } from "express"; export abstract class Controller { public static sendResponse( res: Response, data: any, status: number ): Response { return res.status(status).json(data); } }
AdLibValidator.ts
import { AdLibProps } from "../../ts/types/AdLibProps"; export class AdLibValidator { constructor() { this.invalidProperties = []; } public validate(data: AdLibProps): boolean { if (!this.isValidTimeStamp(data.timestamp)) { this.addToInvalidProperties("timestamp"); } if (!this.isValidPage(data.page)) { this.addToInvalidProperties("page"); } if (this.isValidPagination(data.pagination)) { this.addToInvalidProperties("pagination"); } return this.getInvalidProperties.length === 0; } private isValidTimeStamp(timestamp: Date): boolean { const currentTime = new Date(); if (currentTime < timestamp) { return false; } return true; } private isValidPage(page: number): boolean { return true; } private isValidPagination(pagination: number): boolean { return true; } public addToInvalidProperties(property: string): void { this.invalidProperties.push(property); } public getInvalidPropertiesAsString(): string { return this.invalidProperties.join(", "); } public getInvalidProperties(): string[] { return this.invalidProperties; } private invalidProperties: string[]; }
AdLibRouter.ts
import express, { Router } from "express"; import { RouterErrorHandler } from "../../errors/RouterErrorHandler"; import { AdLibController } from "./AdLibController"; export class AdLibRouter { public static init(): Router { const controller = new AdLibController(); AdLibRouter.router .route("/") .get(controller.getLibs) .all(RouterErrorHandler.methodNotAllowed); return AdLibRouter.router; } private static router: Router = express.Router(); }
解决方案
错误根源是Express路由绑定方法时丢失了类实例的this上下文:当把controller.getLibs作为回调函数传递给.get()时,方法的this不再指向AdLibController的实例,而是变成undefined(严格模式下)。
以下三种方法可修复问题,均保留非静态写法:
方法1:路由绑定显式绑定this
修改AdLibRouter.ts中的路由绑定代码,用.bind()将方法的this绑定到控制器实例:
public static init(): Router { const controller = new AdLibController(); AdLibRouter.router .route("/") .get(controller.getLibs.bind(controller)) // 显式绑定this .all(RouterErrorHandler.methodNotAllowed); return AdLibRouter.router; }
方法2:构造函数内绑定方法
在AdLibController的构造函数中,将getLibs方法绑定到当前实例,后续传递时不会丢失上下文:
constructor() { super(); this.service = new AdLibService(); this.validator = new AdLibValidator(); // 绑定this到实例 this.getLibs = this.getLibs.bind(this); }
方法3:使用箭头函数定义方法(推荐)
将getLibs改为箭头函数,箭头函数会自动捕获类实例的this上下文,无需手动绑定:
// 替换原getLibs方法定义 public getLibs = async (req: Request, res: Response, next: NextFunction) => { const data: AdLibProps = this.transformRequest(req); if (!this.validator.validate(data)) { return next({ status: 400, message: `These properties are not valid: ${this.validator.getInvalidPropertiesAsString()}`, }); } try { const foundAdLibs = await this.service.getLibs( new Date(req.query.timestamp as string), data.page, data.pagination ); return AdLibController.sendResponse(res, foundAdLibs, 200); } catch (e: unknown) { const error = ErrroHandler.ensureError(e); return next({ status: 400, message: error.message, }); } }
补充说明
静态写法能运行的原因是:静态方法/属性属于类本身,不需要依赖实例的this上下文,但静态写法不适合需要维护实例状态的场景(比如每个控制器实例需要独立的validator或service实例)。
内容的提问来源于stack exchange,提问作者Anthony McLamb
相关产品推荐
相关产品推荐

