TypeScript类属性装饰器无法正确覆盖实例的Get/Set方法
问题原因
你写的属性装饰器是给类的原型对象添加了bar的getter,但类中bar = 0的写法属于实例属性初始化——TypeScript会把这段代码编译到类的构造函数里,执行this.bar = 0,这会在实例本身上创建一个同名的自有属性。由于JS中实例自有属性的优先级高于原型属性,所以访问foo.bar时会直接读取实例上的0,而不是原型上的getter返回的42。
解决方案
以下是几种可行的解决方式,你可以根据场景选择:
方案1:将类属性改为访问器(getter)
把实例属性改成原型访问器,这样构造函数不会给实例赋值自有属性,装饰器可以直接覆盖原型上的访问器逻辑:
function Decorator() { return function (target: Object, propertyKey: string) { Object.defineProperty(target, propertyKey, { get: function () { return 42; }, configurable: true, enumerable: true }); }; } export class Foo { @Decorator() get bar() { return 0; // 原逻辑会被装饰器覆盖 } } const foo = new Foo(); console.log(foo.bar); // 输出42
方案2:修改构造函数,实例初始化后覆盖属性
如果必须保留bar = 0的实例属性写法,可以通过修改类的构造函数,在实例创建完成后重新定义实例上的属性,覆盖构造函数设置的自有值:
function Decorator() { return function (target: any, propertyKey: string) { const originalConstructor = target.constructor; // 替换原构造函数 target.constructor = function (...args: any[]) { const instance = new originalConstructor(...args); // 给实例添加getter,覆盖自有属性 Object.defineProperty(instance, propertyKey, { get: () => 42, configurable: true }); return instance; }; // 维持原型链的一致性 target.constructor.prototype = originalConstructor.prototype; }; } export class Foo { @Decorator() bar = 0; } const foo = new Foo(); console.log(foo.bar); // 输出42
方案3:类装饰器批量处理属性(多属性场景)
先通过属性装饰器标记需要处理的属性,再用类装饰器统一修改构造函数,批量处理所有标记的属性:
// 存储类与需要装饰的属性的映射 const targetPropsMap = new Map<Function, string[]>(); function Decorator() { return function (target: any, propertyKey: string) { const constructor = target.constructor; if (!targetPropsMap.has(constructor)) { targetPropsMap.set(constructor, []); } targetPropsMap.get(constructor)!.push(propertyKey); }; } // 类装饰器:应用属性装饰逻辑 function ApplyPropertyDecorators() { return function (constructor: Function) { const originalConstructor = constructor as any; const newConstructor = function (...args: any[]) { const instance = new originalConstructor(...args); // 批量处理所有标记的属性 const props = targetPropsMap.get(constructor); if (props) { props.forEach(prop => { Object.defineProperty(instance, prop, { get: () => 42, configurable: true }); }); } return instance; }; // 保持原型继承关系 newConstructor.prototype = originalConstructor.prototype; return newConstructor as any; }; } @ApplyPropertyDecorators() export class Foo { @Decorator() bar = 0; @Decorator() baz = 100; } const foo = new Foo(); console.log(foo.bar); // 输出42 console.log(foo.baz); // 输出42
内容的提问来源于stack exchange,提问作者user18156
相关产品推荐
相关产品推荐

