TypeScript装饰器能否自动推断或引用所属源类,免去手动指定泛型参数?
无手动泛型的装饰器实现方案
原实现需要手动传泛型的核心原因
当前代码把泛型T声明在了装饰器工厂的外层函数上,TypeScript 的执行顺序是先运行MyDecorator(getOtherValue)拿到最终的属性装饰器函数,再把这个装饰器应用到目标类的属性上。
工厂函数执行的阶段,TS 完全没有后续装饰器要挂载的类的上下文,根本不知道T应该是什么类型,只能强制要求使用者手动传入泛型实参,否则就只能推导成宽泛的 unknown/any,失去类型校验意义。
调整后的可自动推导的实现
调整核心是把泛型的推导时机后移,放到装饰器实际被应用的执行阶段,借助 TS 对装饰器的上下文类型识别能力,自动完成类类型的推导,全程不需要手动传泛型。
调整后的完整代码:
// 外层工厂仅负责接收用户传入的取值回调,不需要提前绑定泛型 function MyDecorator(getOtherValue: (e: any) => string) { // 返回的属性装饰器函数声明泛型,自动从装饰器的应用位置推导所属类的类型 return <T>(target: T, key: keyof T) => { // 装饰器内部拿到带准确类型的target,执行自定义逻辑 const otherValue = getOtherValue(target); // 这里可以直接基于拿到的值做属性描述符修改、元数据绑定等操作 } }
使用者的使用方式完全不需要额外传泛型:
class MyClass { // TS会自动把回调里的e推导为MyClass类型,写错属性会直接报类型错误 @MyDecorator(e => e.otherProperty) myProperty: string; otherProperty: string; }
推导生效的逻辑
当装饰器被应用到类的属性上时,TS 已经明确知道第一个参数target是类的原型对象,会自动把原型的类型赋值给泛型T;再把用户之前传入的getOtherValue回调的参数类型和T做匹配,全链路完成类型推导,和手动写<MyClass>的类型校验效果完全一致:
- 回调里访问不存在的类属性时,TS 会直接抛出编译错误
- 写对属性时会有完整的类型提示
类型严格度优化
如果觉得初始的getOtherValue参数写any过于宽松,可以通过类型关联把校验逻辑收紧,避免装饰器内部拿不到准确类型:
function MyDecorator<TTarget>(getOtherValue: (e: TTarget) => string) { return (target: TTarget, key: keyof TTarget) => { const otherValue = getOtherValue(target); // 自定义逻辑 } }
这种写法下,TS 会从装饰器的target参数位置反向推导TTarget的实际类型,再和用户传入的回调参数类型做匹配,全程没有手动传参的步骤。
注意事项
- 该自动推导能力需要 TypeScript 4.0 及以上版本支持,低版本 TS 对装饰器的上下文类型识别不完整,可能需要做适配
- 如果装饰器需要在类实例创建后读取实际属性值,可以在装饰器内部通过重写属性的getter/setter实现,不需要在类定义阶段提前绑定类类型
- 如果当前项目开启了
noImplicitAny严格检查,需要把回调参数的类型显式和推导的泛型做绑定,避免触发严格模式报错
内容的提问来源于stack exchange,提问作者Quangdao Nguyen
相关产品推荐
相关产品推荐

