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

Angular属性装饰器开发求助:标记组件属性遇只读属性报错

解决Angular装饰器标记属性时的只读属性错误

错误原因

你遇到的Uncaught TypeError: Cannot assign to read-only property 'Symbol(fields)' of object '[object Object]',是因为Angular编译后会冻结组件类的原型对象,直接给原型添加Symbol属性会触发只读限制,之前的实现方式直接修改了类的原型属性,导致报错。

正确实现方案

改用TypeScript内置的Reflect.metadata API存储元数据,这是官方推荐的元数据存储方式,不会触发只读属性限制。

1. 定义@OptionType装饰器

import 'reflect-metadata';

// 定义元数据唯一标识键
const OPTION_TYPE_METADATA_KEY = Symbol('OptionType');

export function OptionType(type: string) {
  return function (target: any, propertyKey: string) {
    // 先获取类上已有的元数据,无则初始化空Map
    const existingOptions = Reflect.getMetadata(OPTION_TYPE_METADATA_KEY, target.constructor) || new Map<string, string>();
    existingOptions.set(propertyKey, type);
    // 将更新后的元数据绑定到类的构造函数上
    Reflect.defineMetadata(OPTION_TYPE_METADATA_KEY, existingOptions, target.constructor);
  };
}

2. 实现getAllProperties函数

export function getAllProperties(componentInstance: any): Map<string, string> {
  // 从组件实例的构造函数上读取元数据
  return Reflect.getMetadata(OPTION_TYPE_METADATA_KEY, componentInstance.constructor) || new Map();
}

3. 组件中使用示例

import { Component, OnInit } from '@angular/core';
import { OptionType, getAllProperties } from './option-type.decorator';

@Component({
  selector: 'app-dashboard',
  template: `<p>Dashboard Component</p>`
})
export class DashboardComponent implements OnInit {
  @OptionType('ColorOption')
  color: string = "";

  @OptionType('SizeOption')
  size: number = 10;

  ngOnInit(): void {
    console.log(getAllProperties(this)); // 返回Map(2) {"color" => "ColorOption", "size" => "SizeOption"}
  }
}

替代方案(依赖Angular内部API,不推荐生产环境)

如果项目禁用了Reflect,可以临时利用Angular内部组件定义扩展元数据,但该方式存在版本更新失效风险:

import { ɵComponentDef as ComponentDef } from '@angular/core';

export function OptionType(type: string) {
  return function (target: any, propertyKey: string) {
    // 延迟执行确保组件定义已初始化
    setTimeout(() => {
      const componentDef = (target.constructor as any).ɵcmp as ComponentDef<any>;
      if (!componentDef.options) componentDef.options = {};
      if (!componentDef.options.optionTypes) componentDef.options.optionTypes = new Map();
      componentDef.options.optionTypes.set(propertyKey, type);
    });
  };
}

export function getAllProperties(componentInstance: any): Map<string, string> {
  const componentDef = (componentInstance.constructor as any).ɵcmp as ComponentDef<any>;
  return componentDef.options?.optionTypes || new Map();
}

注意:内部API无官方维护承诺,优先使用Reflect.metadata方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 16:23:19