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

TypeScript属性装饰器异常行为排查:为何输出不符合预期?

类属性装饰器失效:输出undefined而非预期值的原因与解决办法

问题场景

我定义了如下类和属性装饰器:

Component类

class Component {
  @id() instanceId: string;

  @id() secondaryId: string;

  log() {
    console.log(this.instanceId, this.secondaryId);
  }
}

id属性装饰器

const id = (): PropertyDecorator => {
  return (target, name) => {
    const descriptor = {
      get(this: any) {
        const propertyName = `__${String(name)}`;

        if (!this[propertyName]) {
          this[propertyName] = 'MY ID';
        }

        return this[propertyName];
      },
      enumerable: true,
      configurable: true
    };

    Object.defineProperty(target, name, descriptor);
  };
};

TypeScript配置(tsconfig.json)

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "strictPropertyInitialization": false,
    "target": "ESNext",
    "module": "CommonJS",
    "moduleResolution": "node",
    "types": [
      "node"
    ]
  }
}

执行以下测试代码:

const cmp = new Component();
cmp.log();

预期控制台输出MY ID MY ID,但实际得到undefined undefined。该代码在TS Playground中正常运行,但本地环境及CodeSandbox中异常。将target改为ES2021后,输出恢复正常。

原因分析

问题根源在于TypeScript的useDefineForClassFields配置项:

  • 当target设为ESNext时,TypeScript默认开启useDefineForClassFields: true,这会按照ES标准的类字段定义规则编译代码。
  • 开启该配置后,类中声明的instanceId和secondaryId会被编译为在构造函数中执行this.instanceId = undefined的逻辑,这会直接覆盖装饰器通过Object.defineProperty为属性设置的getter,导致访问属性时直接返回undefined,而非触发装饰器定义的getter逻辑。
  • 当target设为ES2021或更低版本时,TypeScript默认将useDefineForClassFields设为false,类字段会采用传统的编译方式,不会覆盖装饰器设置的getter,因此装饰器能正常工作。

解决办法

有两种可行的解决方案:

  1. 降低target版本
    将tsconfig.json中的target改为ES2021或更低版本,此时useDefineForClassFields会自动设为false,装饰器逻辑即可正常生效。

  2. 显式关闭useDefineForClassFields
    如果需要保留target: ESNext,可以在compilerOptions中显式添加"useDefineForClassFields": false,强制关闭ES标准的类字段编译行为:

{
  "compilerOptions": {
    // ... 其他原有配置
    "target": "ESNext",
    "useDefineForClassFields": false
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 04:45:39