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

如何在TypeScript的Controller构造函数中注入Service?

问题描述

我正在用TypeScript开发一个简单的REST API,调用层级为:Controller > Service > Repository。我尝试了几种写法,但前两种总会报错,只有第三种能正常运行,想请教问题出在哪,最初的写法是否可行?

第一种写法(报错)

Controller

export class GetNurseController {
  constructor(private getNurseService: GetNurseService) {}

  async handle(req: Request, res: Response): Promise<Response> {
    try {
      const { id } = req.authrocket.currentUser;
      const user = await this.getNurseService.execute(id);

      return res.json({ user });
    } catch (err: any) {
      return res.status(500).json({ err });
      // 返回空err对象,但控制台打印getNurseService未定义错误
    }
  }
}

Router

const nurseRepository = new NurseRepository();
const getNurseService = new GetNurseService(nurseRepository);
const getNurseController = new GetNurseController(getNurseService);

const nurseRoutes = Router();
nurseRoutes.get('/', requireLogin, getNurseController.handle);

第二种写法(报错)

Controller

export class GetNurseController {
  public NurseRepository: INurseRepository;
  public getNurseService: GetNurseService;

  constructor() {
    this.nurseRepository = new NurseRepository();
    this.getNurseService = new GetNurseService(this.nurseRepository);
  }

  async handle(req: Request, res: Response): Promise<Response> {
    try {
      const { id } = req.authrocket.currentUser;
      const user = await this.getNurseService.execute(id);

      return res.json({ user });
    } catch (err: any) {
      return res.status(500).json({ err });
    }
  }
}

Router

const getNurseController = new GetNurseController();

const nurseRoutes = Router();
nurseRoutes.get('/', requireLogin, getNurseController.handle);

第三种写法(正常运行)

Controller

export class GetNurseController {
  async handle(req: Request, res: Response): Promise<Response> {
    try {
      const nurseRepository = new NurseRepository();
      const getNurseService = new GetNurseService(nurseRepository);

      const { id } = req.authrocket.currentUser;
      const user = await getNurseService.execute(id);

      return res.json({ user });
    } catch (err: any) {
      return res.status(500).json({ err });
    }
  }
}

Router

const getNurseController = new GetNurseController();

const nurseRoutes = Router();
nurseRoutes.get('/', requireLogin, getNurseController.handle);

问题原因与解决方案

1. 第一种写法的核心问题:this指向丢失

当你把getNurseController.handle直接作为路由处理函数传递时,handle方法内部的this不再指向GetNurseController实例,而是变成了路由回调的上下文(通常是req或undefined),所以会出现getNurseService未定义的错误。

修复方案(任选其一):

  • 手动绑定this:在Router中传递方法时绑定实例上下文:
    nurseRoutes.get('/', requireLogin, getNurseController.handle.bind(getNurseController));
    
  • 用箭头函数包裹:通过箭头函数保留this指向:
    nurseRoutes.get('/', requireLogin, (req, res) => getNurseController.handle(req, res));
    
  • 在类中用箭头函数定义方法:直接让方法的this绑定到实例:
    export class GetNurseController {
      constructor(private getNurseService: GetNurseService) {}
    
      handle = async (req: Request, res: Response): Promise<Response> => {
        try {
          const { id } = req.authrocket.currentUser;
          const user = await this.getNurseService.execute(id);
          return res.json({ user });
        } catch (err: any) {
          return res.status(500).json({ err });
        }
      }
    }
    

2. 第二种写法的问题:属性命名不一致

你在类中声明的属性是public NurseRepository: INurseRepository;(首字母大写),但构造函数中赋值的是this.nurseRepository = new NurseRepository();(首字母小写),导致this.getNurseService依赖的this.nurseRepository未定义,进而引发错误。

修复方案:

统一属性命名的大小写:

export class GetNurseController {
  public nurseRepository: INurseRepository; // 改为小写开头
  public getNurseService: GetNurseService;

  constructor() {
    this.nurseRepository = new NurseRepository();
    this.getNurseService = new GetNurseService(this.nurseRepository);
  }

  // 同时需解决this指向问题,参考第一种写法的修复方案
  async handle(req: Request, res: Response): Promise<Response> {
    // ... 原有逻辑
  }
}

3. 关于最初写法的可行性

你的第一种写法完全可行且更符合依赖注入原则,它通过构造函数注入Service依赖,比第三种在方法内部实例化依赖的写法更利于测试和解耦。只要修复this指向的问题,就能正常运行。

第三种写法虽然能运行,但每次请求都会创建新的Repository和Service实例,存在性能损耗,且不利于单元测试(无法Mock依赖),不推荐长期使用。

内容的提问来源于stack exchange,提问作者Heitor Danilo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 13:25:16