You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 02:53:14