TypeScript如何以类型安全的方式为类添加属性(monkey patch)
TypeScript 类 Monkey Patch 类型安全实现方案
问题本质
类装饰器在运行时会修改类原型挂载属性,但TypeScript类型系统默认不会感知装饰器对类结构的修改,因此会抛出「目标类型上不存在对应属性」的编译错误。
实现方案
方案1:类内补充属性类型声明(最简单)
不需要修改装饰器逻辑,直接在被装饰的类内部用declare关键字声明新增属性的类型即可,不会生成额外运行时代码,仅做类型补充:
function extended(target: any) { const newProperty: string = 'hello'; target.prototype.newProperty = newProperty; } @extended class Test { // 仅做类型声明,无运行时逻辑 declare newProperty: string; } const test = new Test(); // 类型推导正常,无编译错误 console.log(test.newProperty);
方案2:类型安全的装饰器封装(无需每个类手动声明)
给装饰器补充正确的类型定义,让其返回合并了新增属性的类构造函数类型,不需要在每个被装饰类里重复写声明:
// 定义新增属性的类型结构 type ExtendedInstance = { newProperty: string; }; function extended<T extends new (...args: any[]) => object>(target: T) { const newProperty: string = 'hello'; target.prototype.newProperty = newProperty; // 将返回的构造函数类型断言为合并了新增属性的类型 return target as T & new (...args: ConstructorParameters<T>) => InstanceType<T> & ExtendedInstance; } // 直接用函数包裹类定义即可获得完整类型,不需要依赖实验性装饰器配置 const Test = extended(class Test { // 类原有业务逻辑 }); const test = new Test(); // 自动推导newProperty为string类型,无编译错误 console.log(test.newProperty);
注意:TypeScript 5.0+ 启用的标准TC39装饰器不支持装饰器直接修改被装饰目标的类型,如果要使用
@extended装饰器语法糖实现自动类型推导,需要在tsconfig中开启experimentalDecorators编译选项,使用旧版实验性装饰器实现。
不推荐方案:全局类型合并
如果要给所有对象批量挂载属性,可以通过全局接口合并给Object补充类型,但这种方式会污染全局类型定义,导致所有对象实例都被推导存在该属性,容易引发类型误判:
declare global { interface Object { newProperty: string; } }
内容的提问来源于stack exchange,提问作者Mikelgo
相关产品推荐
相关产品推荐

