You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为TypeScript类属性添加toPropertyName()扩展方法?

解决方案:给TypeScript对象属性添加toPropertyName()方法

要实现调用p.firstName.toPropertyName()返回属性名"firstName",核心难点在于原始类型(如string)的值本身不携带所属属性的上下文信息,直接给String.prototype扩展方法无法区分属性归属。以下是两种可行的实现方案:

方案一:使用Proxy代理对象(通用型)

通过Proxy拦截属性访问,为每个属性值包装一个带toPropertyName()方法的对象,同时保留原始类型的所有行为(如字符串的toUpperCase()、插值运算等)。

步骤1:定义Person类

export class Person {
  firstName: string;
  lastName: string;

  constructor(firstName: string, lastName: string) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}

步骤2:实现Proxy生成函数

function createNamedPropertyProxy<T extends object>(obj: T): T {
  return new Proxy(obj, {
    get(target, prop: string | symbol) {
      // 仅处理对象自身的字符串属性
      if (typeof prop === 'string' && prop in target) {
        const value = target[prop as keyof T];
        
        // 处理原始类型(string/number/boolean等)
        if (typeof value !== 'object' || value === null) {
          // 创建继承自原始类型原型的包装对象,保留原方法
          const wrapper = Object.create(Object.getPrototypeOf(value));
          Object.assign(wrapper, value);
          
          // 添加toPropertyName方法,返回当前属性名
          wrapper.toPropertyName = () => prop;
          
          // 确保包装对象在原始类型操作中表现正常(如字符串插值、数值运算)
          Object.defineProperty(wrapper, Symbol.toPrimitive, {
            value: () => value
          });
          
          return wrapper;
        } 
        // 递归处理嵌套对象
        else {
          return createNamedPropertyProxy(value);
        }
      }
      
      return (target as any)[prop];
    }
  });
}

步骤3:在Angular组件中使用

// 实例化并包装Person对象
const p = createNamedPropertyProxy(new Person('John', 'Doe'));

// 调用toPropertyName获取属性名
console.log(p.firstName.toPropertyName()); // 输出 "firstName"
console.log(p.lastName.toPropertyName()); // 输出 "lastName"

// 原始类型方法仍可正常使用
console.log(p.firstName.toUpperCase()); // 输出 "JOHN"
console.log(`${p.firstName} ${p.lastName}`); // 输出 "John Doe"

方案二:使用Getter手动包装(轻量型)

如果Person类的属性较少,可以直接通过getter返回带toPropertyName()的包装对象,无需Proxy。

修改Person类

export class Person {
  private _firstName: string;
  private _lastName: string;

  constructor(firstName: string, lastName: string) {
    this._firstName = firstName;
    this._lastName = lastName;
  }

  get firstName() {
    return {
      value: this._firstName,
      toPropertyName: () => 'firstName',
      toString: () => this._firstName,
      toUpperCase: () => this._firstName.toUpperCase(),
      // 根据需要添加其他string方法
    };
  }

  get lastName() {
    return {
      value: this._lastName,
      toPropertyName: () => 'lastName',
      toString: () => this._lastName,
      toUpperCase: () => this._lastName.toUpperCase(),
    };
  }
}

使用方式

const p = new Person('John', 'Doe');
console.log(p.firstName.toPropertyName()); // 输出 "firstName"
console.log(p.firstName.toUpperCase()); // 输出 "JOHN"

关键说明

  • 为什么不能直接给String.prototype加方法?因为同一个字符串值可能被多个属性引用(如p.firstName = "John"、p.lastName = "John"),此时无法通过字符串自身区分所属属性。
  • Proxy方案的优势是通用、无需修改原有类结构,支持任意数量的属性和嵌套对象;Getter方案则更轻量,但需要手动维护每个属性的包装逻辑。

内容的提问来源于stack exchange,提问作者Dhimant Bhensdadia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 13:43:10