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

TypeScript+Express+Tsyringe构建API遇router未定义错误求助

TypeError: Cannot read properties of undefined (reading 'router') 问题解决

我正在使用TypeScript和ExpressJS,采用整洁架构构建类式RESTful API,同时练习Typegoose和Tsyringe以学习该语言与框架下的依赖注入实现。但访问路径时触发TypeError: Cannot read properties of undefined (reading 'router')错误。

相关代码

Product 模型

import { getModelForClass, prop } from '@typegoose/typegoose';

export class ProductModel {

  @prop()
  private timestamp: number = Date.now();

  @prop({ required: true })
  private title!: string;

  @prop({ required: true })
  private description!: string;

  @prop({ required: true })
  private code!: number;

  @prop({ required: true })
  private picture!: string;

  @prop({ required: true })
  private price!: number;

  @prop({ required: true })
  private stock!: number;

}

export const Product = getModelForClass(ProductModel);

Database Repository 接口

import { Id, Query } from '../commons/commons';

export interface DatabaseRepository<T> {

  getAll(query?: Query): Promise<T[]>;

}

MongoDB 适配器

import { Product, ProductModel } from '../../../../domain/models/product/Product';
import { DatabaseRepository } from '../../../../application/repository/database.repository';
import { Id, Query } from '../../../../application/commons/commons';
import { injectable } from 'tsyringe';
import { Logger } from '../../../../application/config/logger/logger';

@injectable()
export class ProductMongoDBAdapter implements DatabaseRepository<ProductModel> {

  async getAll(query?: Query): Promise<ProductModel[]> {
    try {
      return Product.find({});
    } catch (error) {
      Logger.error(error);
      throw (error);
    }
  }
}

UseCase

import { autoInjectable, inject } from 'tsyringe';
import { DatabaseRepository } from '../../../application/repository/database.repository';
import { ProductModel } from '../../models/product/Product';

@autoInjectable()
export class GetAllProductsUseCase {

  constructor(@inject('DatabaseRepository') private databaseRepository: DatabaseRepository<ProductModel>) {
  }

  public async getAllProducts(): Promise<ProductModel[]> {
    return this.databaseRepository.getAll();
  }
}

Controller

import { autoInjectable } from 'tsyringe';
import * as USE_CASE from '../../../../domain/usecases';
import { Logger } from '../../../../application/config/logger/logger';
import { Request, Response } from 'express';

@autoInjectable()
export class ProductHandler {

  constructor(private readonly getAllProductsUseCase: USE_CASE.GetAllProductsUseCase) {
  }

  public async getAllProducts(req: Request, res: Response): Promise<Response> {
    try {
      const products = await this.getAllProductsUseCase.getAllProducts();
      return res.status(200).json(products);
    } catch (error) {
      Logger.error(error);
      throw(error);
    }
  }
}

Product Router

import { Router } from 'express';
import * as PRODUCT_HANDLER from '../../rest-controller/';
import { autoInjectable } from 'tsyringe';

@autoInjectable()
export class ProductRouter {

  private readonly router: Router = Router();

  constructor(private readonly productHandler: PRODUCT_HANDLER.ProductHandler) {
  }

  public routes() {
    this.router
        .route('/api/products')
        .get(this.productHandler.getAllProducts);
  }
}

主路由配置

import { Router } from 'express';
import { autoInjectable } from 'tsyringe';
import { ProductRouter } from './product/product.routes';

@autoInjectable()
export default class Routes {
  private router: Router = Router();

  constructor(private readonly productRouter: ProductRouter) {
  }

  public routes() {
    return this.router.use('/', this.productRouter.routes);
  }
}

应用启动类

import 'reflect-metadata';
import express, { Application, json } from 'express';
import morgan from 'morgan';
import cors from 'cors';
import { Server } from 'http';
import { Gen } from './commons/commons';
import { Logger } from './config/logger/logger';
import DbConnection from './config/database/DbConnection';
import Config from './config/Config';
import Routes from '../infrastructure/entry-points/routes/routes';
import { autoInjectable, container } from 'tsyringe';
import { ProductMongoDBAdapter } from '../infrastructure/driven-adapters/mongo-adapter/product/product.mongodb.adapter';

@autoInjectable()
export default class ServerApplication {
  private app: Application = express();
  private routes: Routes;

  constructor() {
    container.registerInstance('DatabaseRepository', new ProductMongoDBAdapter());
    this.routes = container.resolve(Routes);
    this.middlewares();
  }

  public init(PORT: Gen): Server {
    return this.app.listen(PORT, () => {
      Logger.info(`Open server on http://localhost:${PORT}`);
      DbConnection.connect(`${Config.DB_CONNECTION}`)
                  .then(() => Logger.info('Database connection established successfully'))
                  .catch(err => Logger.error(err));
    });
  }

  private middlewares() {
    this.app.use(cors());
    this.app.use(json());
    this.app.use(morgan('dev'));
    this.app.use('/', this.routes.routes);
  }
}

服务器启动文件

import 'reflect-metadata';
import ServerApplication from './application/ServerApplication';
import Config from './application/config/Config';
import { Gen } from './application/commons/commons';
import { Logger } from './application/config/logger/logger';

export class Server {
  private static PORT: Gen = Config.PORT;
  private static serverApplication: ServerApplication = new ServerApplication();

  public static start(): void {
    try {
      this.serverApplication.init(`${Server.PORT}`);
    } catch (error) {
      Logger.error(error);
    }
  }
}

Server.start();

错误信息

TypeError: Cannot read properties of undefined (reading 'router') at routes (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\src\infrastructure\entry-points\routes\routes.ts:13:17) at Layer.handle [as handle_request] (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\express@4.16.1\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\express@4.16.1\node_modules\express\lib\router\index.js:317:13) at C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\express@4.16.1\node_modules\express\lib\router\index.js:284:7 at Function.process_params (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\express@4.16.1\node_modules\express\lib\router\index.js:335:12) at next (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\express@4.16.1\node_modules\express\lib\router\index.js:275:10) at logger (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\morgan@1.9.1\node_modules\morgan\index.js:144:5) at Layer.handle [as handle_request] (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\express@4.16.1\node_modules\express\lib\router\layer.js:95:5) at trim_prefix (C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\express@4.16.1\node_modules\express\lib\router\index.js:317:13) at C:\Users\juan.cano\Desktop\Study\C-1\BACK-END\ProyectoFinal\EntregaFinal\node_modules\.pnpm\express@4.16.1\node_modules\express\lib\router\index.js:284:7 

问题修复方案

1. 修改ProductRouter,返回Router实例并修复this指向

ProductRouter的routes方法需要返回配置好的Router对象,同时要确保getAllProducts方法执行时this指向正确:

import { Router } from 'express';
import * as PRODUCT_HANDLER from '../../rest-controller/';
import { autoInjectable } from 'tsyringe';

@autoInjectable()
export class ProductRouter {
  private readonly router: Router = Router();

  constructor(private readonly productHandler: PRODUCT_HANDLER.ProductHandler) {}

  public routes(): Router {
    // 用箭头函数绑定this,避免productHandler的this丢失
    this.router
        .route('/api/products')
        .get((req, res) => this.productHandler.getAllProducts(req, res));
    return this.router;
  }
}

2. 修改主Routes,正确调用ProductRouter的routes方法

原代码中直接传递方法引用,需要改为调用方法获取Router实例:

import { Router } from 'express';
import { autoInjectable } from 'tsyringe';
import { ProductRouter } from './product/product.routes';

@autoInjectable()
export default class Routes {
  private router: Router = Router();

  constructor(private readonly productRouter: ProductRouter) {}

  public routes(): Router {
    return this.router.use('/', this.productRouter.routes());
  }
}

3. 修改ServerApplication,正确调用Routes的routes方法

同样需要调用方法获取Router实例,而不是传递方法引用:

private middlewares() {
  this.app.use(cors());
  this.app.use(json());
  this.app.use(morgan('dev'));
  this.app.use('/', this.routes.routes());
}

原因解析

  • ProductRouter的routes方法未返回Router实例,导致主Routes调用后得到undefined,进而Express在尝试访问router属性时报错。
  • 直接传递方法引用会导致执行时this指向丢失,无法正确访问类内部的属性和方法。
  • 所有路由配置方法都需要返回有效的Router对象,才能被Express的use方法正确处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 22:50:28