TypeScript属性装饰器能否访问类的其他属性?
关于TypeScript装饰器检查Point类x和y值关系的问题
咱先直接说结论:你现在给x加的这个属性装饰器,没法直接实现实例化后动态检查x和y大小的需求,得先搞清楚TypeScript装饰器的执行逻辑——属性装饰器是在类定义的阶段就运行了,不是等你new出实例的时候。这时候装饰器能拿到的只是类的原型对象,还有当前属性的描述符,根本碰不到具体实例里的x和y值,哪怕你给x、y设了初始值,那也只是类原型上的默认值,不是每个实例可能被修改后的动态值。
两种场景的解决方案
场景1:只需要检查类定义时的默认初始值
如果你的需求只是确保类里写的x、y默认值符合x≥y的要求,那属性装饰器是可以做到的,直接在装饰器里访问类原型上的y值就行:
function test(myValue: string) { function t(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) { // target是Point类的原型对象 const defaultX = descriptor.value; // 拿到x的默认值6 const defaultY = (target as any).y; // 拿到原型上的y默认值5 if (defaultX < defaultY) { throw new Error(`默认值x(${defaultX})小于y(${defaultY}),不符合要求!`); } } return <any>t; } class Point { @test('admin') x: number = 6; y: number = 5; }
不过要注意,这种写法只会在类定义的时候检查一次默认值,如果你实例化后修改了x或y的值,这个装饰器早就执行完了,根本不会触发检查。
场景2:需要检查实例创建或属性修改时的动态值
如果要在实例创建后,或者每次修改x/y时都检查它们的大小关系,就得换个思路,这里给你两种常用方案:
方案A:用类装饰器在实例创建时检查
类装饰器可以修改类的构造函数,在实例创建完成后做检查:
// 定义类装饰器 function checkXYOnInstance() { return function(target: typeof Point) { // 保存原来的构造函数 const originalConstructor = target; // 重写构造函数 function NewConstructor(...args: any[]) { // 创建实例 const instance = new originalConstructor(...args); // 检查x和y的关系 if (instance.x < instance.y) { throw new Error(`实例的x(${instance.x})小于y(${instance.y}),不符合要求!`); } return instance; } // 保持原型链不变,避免破坏类的继承关系 NewConstructor.prototype = originalConstructor.prototype; return NewConstructor as typeof Point; } } @checkXYOnInstance() class Point { x: number = 6; y: number = 5; } // 实例化时会触发检查 const p1 = new Point(); // 没问题 const p2 = new Point(); p2.y = 7; // 这里不会触发检查,因为构造函数只在创建实例时跑一次
方案B:用访问器(get/set)+装饰器,属性修改时自动检查
如果要每次修改x或y都触发检查,最好把x、y改成访问器属性,然后给setter加装饰器:
// 定义装饰器,给setter添加检查逻辑 function checkXYOnSet() { return function(target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) { // 保存原来的set方法 const originalSet = descriptor.set; // 重写set方法 descriptor.set = function(value: number) { // 先执行原来的set逻辑,设置属性值 originalSet?.call(this, value); // 检查当前实例的x和y if (this.x < this.y) { throw new Error(`x(${this.x})小于y(${this.y}),不符合要求!`); } }; return descriptor; } } class Point { private _x: number = 6; private _y: number = 5; @checkXYOnSet() get x() { return this._x; } set x(value: number) { this._x = value; } @checkXYOnSet() get y() { return this._y; } set y(value: number) { this._y = value; } } const p = new Point(); p.y = 7; // 修改y时会触发检查,直接抛出错误
这样不管是修改x还是y,只要值不符合要求,都会立即抛出错误,完全满足动态检查的需求。
内容的提问来源于stack exchange,提问作者damien marchand
相关产品推荐
相关产品推荐

