如何在普通JavaScript对象的属性上使用TypeScript装饰器?
如何为普通对象属性中的GraphQL解析器应用TypeScript装饰器
问题背景
我们项目里给Apollo GraphQL解析器写了包装函数,用来做认证和前置检查,现在想换成TypeScript装饰器。能找到函数、类/方法上用装饰器的示例:
@decorator function fn() {} @clsDecorator class Cls { @methodDecorator public myMethod() {} }
但我们的解析器是从多个模块收集的,定义成普通JS对象的属性,直接给这些属性加装饰器会触发TypeScript-eslint错误(比如Property assignment expected或Declaration expected),而且装饰器根本没生效:
export const ModuleResolvers = { Query: { // 无效 @decorator queryResolver1(obj, args, context) {} // 无效 @decorator queryResolver2: (obj, args, context) => {} } }
解决方案
TypeScript装饰器目前不支持直接用于对象字面量的属性,但可以通过以下几种方式实现类似效果:
1. 手动调用装饰器函数
装饰器本质就是函数,直接把解析器函数作为参数传给装饰器,再赋值给对象属性,这是最直接的方案,完全兼容现有代码结构:
export const ModuleResolvers = { Query: { queryResolver1: decorator(function(obj, args, context) { // 解析器逻辑 }), queryResolver2: decorator((obj, args, context) => { // 解析器逻辑 }) } }
2. 用类封装解析器后转成对象
如果想保留装饰器的语法糖,可以把解析器写成类的方法,用装饰器修饰后,再把类实例转换成对象:
class QueryResolvers { @decorator queryResolver1(obj, args, context) { // 解析器逻辑 } @decorator queryResolver2(obj, args, context) { // 解析器逻辑 } } export const ModuleResolvers = { Query: new QueryResolvers() }
注意:别用箭头函数定义类方法,因为箭头函数是实例属性而非原型方法,装饰器可能无法正常作用。
3. 写工具函数批量处理解析器
如果有大量解析器需要加装饰器,可以写一个工具函数遍历对象属性,批量应用装饰器:
function applyDecoratorsToResolvers<T extends Record<string, Function>>( resolvers: T, decorator: (fn: Function) => Function ): T { const decorated: Partial<T> = {}; for (const key in resolvers) { if (typeof resolvers[key] === 'function') { decorated[key] = decorator(resolvers[key]) as T[keyof T]; } } return decorated as T; } // 使用示例 const rawQueryResolvers = { queryResolver1(obj, args, context) {}, queryResolver2(obj, args, context) {} }; export const ModuleResolvers = { Query: applyDecoratorsToResolvers(rawQueryResolvers, decorator) };
这种方式能避免重复代码,保持结构整洁。
关键说明
TypeScript的装饰器提案目前主要针对类、类方法、类属性、参数等场景,对象字面量的属性不在官方支持范围内,所以直接用@decorator语法会报错。上述方案里,手动调用装饰器是最贴合现有代码结构、无需大幅重构的选择。
内容的提问来源于stack exchange,提问作者Michael Ef
相关产品推荐
相关产品推荐

