如何使用装饰器重命名TypeScript类实例的属性键?
问题描述
我定义了如下类:
class A { description: string }
我希望创建该类的实例时,能够正常使用description来设置属性,但访问实例时,该属性以a.desc的形式存在。
常规实现方式如下:
class A { desc: string constructor(description: string) { this.desc = description } } const instance = new A("xxx xxx xxx") console.log(instance) // { desc: "xxx xxx xxx" } console.log(instance.desc) // "xxx xxx xxx"
我想通过装饰器实现类似效果,比如:
class A { @RenameKey("desc") description: string }
我了解过@Expose()装饰器,但它会同时修改输入和输出的属性名,而我需要输入时仍用description,输出时改为desc。
请问是否可以使用装饰器重命名类实例的属性键?我知道可以手动这么做:
const a = new A("xxx xxx xxx") a.desc = a.description delete a.description
但希望通过装饰器自动完成这个需求。
解决方案
完全可以通过自定义装饰器实现这个需求,以下提供两种实用方案:
方案一:访问器代理属性(推荐)
通过装饰器在类原型上为description定义getter/setter,将读写操作转发到desc属性,同时保证实例仅存储desc:
function RenameKey(targetKey: string) { return function (target: any, propertyKey: string) { Object.defineProperty(target, propertyKey, { get() { return this[targetKey]; }, set(value: any) { this[targetKey] = value; }, enumerable: false, // 让description不在实例枚举中显示 configurable: true }); }; } // 使用装饰器 class A { desc!: string; @RenameKey("desc") description!: string; } // 测试 const instance = new A(); instance.description = "测试内容"; console.log(instance.desc); // 输出:测试内容 console.log(instance); // 输出:{ desc: "测试内容" } console.log(instance.description); // 输出:测试内容
方案二:实例化后自动迁移属性
如果希望实例化后直接移除description,只保留desc,可以通过装饰器重写类的构造函数,自动完成属性迁移:
function RenameKey(targetKey: string) { return function (target: any, propertyKey: string) { const originalConstructor = target.constructor; // 重写构造逻辑 target.constructor = function (...args: any[]) { const instance = new originalConstructor(...args); // 迁移属性并删除原属性 if (instance.hasOwnProperty(propertyKey)) { instance[targetKey] = instance[propertyKey]; delete instance[propertyKey]; } return instance; }; }; } // 使用装饰器 class A { @RenameKey("desc") description: string; constructor(description: string) { this.description = description; } } // 测试 const instance = new A("测试内容"); console.log(instance); // 输出:{ desc: "测试内容" }
方案对比
- 方案一:保留
description作为读写入口,外部仍可通过description操作,但实例实际存储的是desc,适合需要兼容旧代码的场景。 - 方案二:完全移除原属性,仅保留目标属性,和你手动处理的逻辑完全一致。
内容的提问来源于stack exchange,提问作者Islam Y-
相关产品推荐
相关产品推荐

