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

调试TypeScript类try/catch装饰器:Reflect.ownKeys仅返回constructor

类装饰器无法捕获箭头函数方法的错误?问题出在方法定义方式上

你的核心问题是类里的方法用了箭头函数定义,导致装饰器遍历原型时找不到它,根本没完成方法包装,自然不会触发错误回调。

问题根源

你写的getScorecard是类字段箭头函数,这种写法会把方法挂载到类的实例上,而不是类的prototype原型链上。而你的装饰器逻辑只遍历了target.prototype,所以Reflect.ownKeys(target.prototype)只能拿到constructor,完全碰不到这个箭头函数方法,装饰器的错误包装逻辑根本没作用在它上面。

解决方案

方案1:把箭头函数改成原型方法(推荐)

这是类方法的标准写法,方法会挂载到原型上,你的原有装饰器逻辑就能正常遍历包装:

@TryCatchClassDecorator(EagerLoadingError, (err, context) => {
    console.log(context, err); // 现在会正常打印日志
    throw err;
})
class TaskDAO {
    constructor() {}

    // 改成普通async方法,挂载到原型
    public async getScorecard(providerName: ProviderEnum, batchNum: number) {
        return await Task.findAll({ where: { providerName }, include: { model: Batch, where: { batchId: batchNum } } });
    };
}

方案2:如果必须用箭头函数(比如需要绑定this),修改装饰器逻辑

如果一定要保留箭头函数写法,就得让装饰器在实例化时处理实例的自有属性,而不是只遍历原型:

function TryCatchClassDecorator(errorType: any, handler: (err: any, context: any) => void) {
    return function(target: Function) {
        const originalConstructor = target;

        // 重写构造函数,在实例化时包装箭头函数方法
        function NewConstructor(...args: any[]) {
            const instance = new originalConstructor(...args);

            // 遍历实例的自有属性,找到箭头函数方法
            for (const propName of Object.getOwnPropertyNames(instance)) {
                const propValue = instance[propName];
                // 跳过构造函数,只处理函数类型的属性
                if (typeof propValue === 'function' && propName !== 'constructor') {
                    // 包装异步方法的错误处理
                    instance[propName] = async function(...innerArgs: any[]) {
                        try {
                            return await propValue.apply(this, innerArgs);
                        } catch (err) {
                            if (err instanceof errorType) {
                                handler(err, { method: propName, args: innerArgs });
                            }
                            throw err;
                        }
                    };
                }
            }

            return instance;
        }

        // 保持原型链和类名一致,避免破坏原有结构
        NewConstructor.prototype = originalConstructor.prototype;
        Object.defineProperty(NewConstructor, 'name', { value: originalConstructor.name });

        return NewConstructor as unknown as typeof target;
    };
}

验证效果

修改后,当getScorecard抛出SequelizeEagerLoadingError时,装饰器的回调会正常执行,打印日志后重新抛出错误,你就能看到完整的错误栈信息了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 19:35:24