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

express+tsyringe访问接口报错Cannot read properties of undefined

问题描述

我正在开发学校课程的小型项目,是express与依赖注入技术的初学者,搭建该测试项目是为了熟悉相关技术的运行逻辑,花费数小时排查报错始终未定位到根因。

服务启动后访问根路由时抛出如下错误:

TypeError: Cannot read properties of undefined (reading 'service')
    at controllerMethod (D:\Projects\Typescript\third\dist\app.controller.js:20:14)
    at Layer.handle [as handle_request] (D:\Projects\Typescript\third\node_modules\express\lib\router\layer.js:95:5)
    at next (D:\Projects\Typescript\third\node_modules\express\lib\router\route.js:144:13)
    at Route.dispatch (D:\Projects\Typescript\third\node_modules\express\lib\router\route.js:114:3)
    at Layer.handle [as handle_request] (D:\Projects\Typescript\third\node_modules\express\lib\router\layer.js:95:5)
    at D:\Projects\Typescript\third\node_modules\express\lib\router\index.js:284:15
    at Function.process_params (D:\Projects\Typescript\third\node_modules\express\lib\router\index.js:346:12)
    at next (D:\Projects\Typescript\third\node_modules\express\lib\router\index.js:280:10)
    at Function.handle (D:\Projects\Typescript\third\node_modules\express\lib\router\index.js:175:3)
    at router (D:\Projects\Typescript\third\node_modules\express\lib\router\index.js:47:12) 

已按照tsyringe要求配置装饰器、引入reflect-metadata、通过容器解析控制器实例,仍触发上述报错。

相关代码

app.ts

import express, { json, urlencoded } from 'express'
import { APPROUTE } from './app.route'

const app = express()

app.use(json())
app.use(urlencoded({ extended: false }))
app.use(APPROUTE)

app.listen(5000, ()=>{
    console.log(`connected on port 5000`)
})

app.controller.ts

import { Request, Response } from "express";
import { inject, singleton } from "tsyringe";
import { AppService } from "./app.service";

@singleton()
export class AppController{

    constructor(@inject(AppService) private service: AppService){}

    controllerMethod(req: Request, res: Response){
        this.service.methodService()
    }
}

app.service.ts

import { singleton } from "tsyringe";

@singleton()
export class AppService{

    methodService(){
        console.log('service working')
    }
}

app.route.ts

import 'reflect-metadata'
import { Router } from "express";
import { container } from "tsyringe";
import { AppController } from "./app.controller";

export const APPROUTE = Router()

const controller: AppController = container.resolve<AppController>(AppController)

APPROUTE.get('/', controller.controllerMethod)

package.json

{
  "scripts": {
    "start": "nodemon ./dist/app.js"
  },
  "devDependencies": {
    "nodemon": "^2.0.16"
  },
  "dependencies": {
    "@types/express": "^4.17.13",
    "@types/node": "^18.0.0",
    "dotenv": "^16.0.1",
    "express": "^4.18.1",
    "reflect-metadata": "^0.1.13",
    "tsyringe": "^4.7.0"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "es6",                                 
    "experimentalDecorators": true,                  
    "emitDecoratorMetadata": true,
    "module": "commonjs",                                
    "rootDir": "./src",                                  
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "strictPropertyInitialization": false,
    "skipLibCheck": true
  }
}
根因与修复方案

根因

报错是类方法作为回调传递时的this上下文丢失问题。直接将controller.controllerMethod作为处理函数传给Express路由时,Express触发回调不会绑定controller实例作为执行上下文,方法运行时内部this为undefined,因此无法读取挂载在this上的service属性。依赖注入本身的配置没有问题。

修复方法

任选一种方式修改路由注册逻辑即可:

  • 方式1:显式绑定this上下文
    将app.route.ts中的路由注册行修改为:
    APPROUTE.get('/', controller.controllerMethod.bind(controller))
    
  • 方式2:用箭头函数包裹调用,保留实例上下文
    将路由注册行修改为:
    APPROUTE.get('/', (req, res) => controller.controllerMethod(req, res))
    

额外配置优化

reflect-metadata需要在应用最入口文件的最顶部引入,建议将import 'reflect-metadata'移到app.ts的第一行,不要仅在路由文件中引入,避免后续模块加载时因元数据读取顺序问题导致依赖注入失效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:06:23