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

NestJS依赖注入存储在Map中的类方法为何未定义?

问题描述

我创建了一个EventService,用来存储通过自定义装饰器添加的自定义事件。目前流程看似正常,但存在一个问题:当尝试获取存储的事件类时,其内部方法全是undefined。奇怪的是,类本身看起来已经完成初始化了。请问可能的原因是什么?


event.service.ts

@Injectable()
/**
 * @class EventService
 */
export class EventService {
    /**
     * @private
     * @readonly
     * @type {Logger}
     */
    private readonly logger: Logger = new Logger(EventService.name);

    /**
     * Event Storage
     *
     * @private
     * @readonly
     * @type {Map<string, BaseEvent>}
     */
    private readonly _events: Map<string, BaseEvent> = new Map<
        string,
        BaseEvent
    >();

    /**
     * Add Event
     *
     * @public
     * @async
     * @param {string} key Event Name
     * @param {BaseEvent} event Event Class
     * @param {boolean} overwrite Overwrite Event in Map
     * @returns {Promise<void>}
     */
    public async add(
        key: string,
        event: BaseEvent,
        overwrite = false
    ): Promise<void> {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const className = (event as any).name;
        const hasRegistered = await this.has(key);

        if (hasRegistered && !overwrite) {
            this.logger.error(`${key} already registered with: ${className}`);
            return;
        }

        if (hasRegistered && overwrite) {
            this._events.set(key, event);
            this.logger.log(
                `${key} got overwritten by event class: ${className}`
            );
            return;
        }

        this._events.set(key, event);

        console.log(event);
        console.log(event.test);

        this.logger.log(`${key} was registered to: ${className}`);
    }
}

event.decorator.ts

/**
 * Bind Event to Class
 *
 * @function
 * @param {string} name Event Name
 * @param {boolean} client Determinate if its a client event
 * @param {string} overwrite Overwrite existing event
 */
export function Event(name: string, client = false, overwrite = false): any {
    return (constructor: any) => {
        return class extends constructor {
            constructor(...args: any[]) {
                super(args);

                const eventService: EventService = this.eventService[0];
                eventService.add(name, constructor, overwrite);
            }
        };
    };
}

player-join.event.ts

@Injectable()
@Event('playerJoin')
export class PlayerJoinEvent extends BaseEvent {
    /**
     * @private
     * @readonly
     * @type {Logger}
     */
    public readonly logger: Logger = new Logger(PlayerJoinEvent.name);

    /**
     * Handle Player Join
     *
     * @public
     * @async
     * @param {any} player Joining Player
     * @returns {Promise<void>}
     */
    public async execute(player: string): Promise<void> {
        this.logger.log(`${player} joined the server`);
    }

    public test(): void {
        this.logger.debug('hi');
    }
}

控制台输出

[Nest] 22508  - 20.09.2022, 19:44:48     LOG [NestFactory] Starting Nest application...
[Nest] 22508  - 20.09.2022, 19:44:48     LOG [InstanceLoader] AppModule dependencies initialized +24ms
[class PlayerJoinEvent extends BaseEvent] // these both logs are in the EventService.ts after adding them
undefined
[Nest] 22508  - 20.09.2022, 19:44:48     LOG [EventService] playerJoin was registered to: PlayerJoinEvent

问题原因与解决办法

核心问题

你在装饰器里传给EventService.add的是类的构造函数(constructor),而不是类的实例对象。构造函数本身不会包含实例方法——实例方法是类被实例化后,挂载在实例对象上的,所以你访问event.test自然会得到undefined。

看装饰器里的关键代码:

eventService.add(name, constructor, overwrite);

这里的constructor就是PlayerJoinEvent这个类本身,不是它的实例。

解决步骤

  1. 修改装饰器,传入类实例
    把装饰器里的constructor换成this(当前类的实例),同时修正super的参数传递方式(展开args而不是直接传数组):
export function Event(name: string, client = false, overwrite = false): any {
    return (constructor: any) => {
        return class extends constructor {
            constructor(...args: any[]) {
                super(...args); // 展开参数,避免父类构造函数收到数组

                const eventService: EventService = this.eventService[0];
                // 传入当前实例,而非构造函数
                eventService.add(name, this, overwrite);
            }
        };
    };
}
  1. 调整EventService.add的类名获取方式
    原来通过(event as any).name获取的是构造函数的名称,现在传入的是实例,需要改成从实例的constructor属性获取类名:
// 替换原来的className获取逻辑
const className = event.constructor.name;

这样修改后,EventService存储的就是类的实例,实例上的test、execute等方法就能正常访问了。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 00:50:35