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

TypeScript 5属性装饰器转换报错:context.addInitializer不是函数

问题描述

我有如下旧版PropertyDecorator:

const bound = (method: any): PropertyDecorator =>
    ((target: any, name?: PropertyKey): any => {
      if(name === undefined) {
        throw new Error('Bound decorator must be used with a property name.');
      }

      return {
        get(): any {
          return target[method].bind(this);
        }
      };
    });

class Foo {

  @bound('bar')
  foo!: any;


  bar(): string {
    return 'bar';
  }

}

想要将其转换为TypeScript 5装饰器,我的尝试代码如下:

const bound = (<
    This,
    Value
>(method: keyof This): any => ((
    _value: undefined,
    context: ClassFieldDecoratorContext<This, Value>
) => {
  if(context.private) {
    throw new Error('Bound decorator cannot be used on private properties.');
  }

  context.addInitializer(function(this: This) {
    (this as any)[method] = (this[method] as CallableFunction).bind(this);
  });
}));

该装饰器编译后的JS输出为:

const o = (i) => (n, r) => {
  if (r.private)
    throw new Error("Bound decorator cannot be used on private properties.");
  r.addInitializer(function() {
    this[i] = this[i].bind(this);
  });
};
export {
  o as bound
};

运行时出现错误:TypeError: context.addInitializer is not a function,我的tsconfig配置如下:

{
    "compilerOptions": {
        "allowUnreachableCode": false,
        "allowUnusedLabels": false,
        "alwaysStrict": true,
        "exactOptionalPropertyTypes": true,
        "noFallthroughCasesInSwitch": true,
        "noImplicitAny": true,
        "noImplicitOverride": true,
        "noImplicitReturns": true,
        "noImplicitThis": true,
        "noPropertyAccessFromIndexSignature": true,
        "noUncheckedIndexedAccess": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "strict": true,
        "strictBindCallApply": true,
        "strictFunctionTypes": true,
        "strictNullChecks": true,
        "strictPropertyInitialization": true,
        "useUnknownInCatchVariables": true,
        "module": "es6",
        "moduleResolution": "node10",
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "lib": [
            "es2017",
            "dom"
        ],
        "target": "es2017",
        "skipLibCheck": true,
        "composite": true,
        "rootDir": "./src",
        "outDir": "./dist"
    },
    "files": [
        "./src/bound.ts",
        "./src/index.ts"
    ],
    "include": [
        "./src/**/*.ts"
    ],
    "exclude": [
        "./node_modules",
        "./dist"
    ]
}

问题原因与解决方案

错误根源

你启用了旧版装饰器开关experimentalDecorators,但编写的是TypeScript 5的新装饰器语法,两者完全不兼容。旧版装饰器的上下文对象没有addInitializer方法,因此触发报错。

修正步骤

1. 调整tsconfig配置

关闭旧版装饰器,启用新版装饰器模式:

{
    "compilerOptions": {
        // 保留原有严格模式等配置
        "experimentalDecorators": false, // 关闭旧版装饰器
        "emitDecoratorMetadata": false, // 新版装饰器暂不支持该特性,必须关闭
        "module": "ESNext", // 新版装饰器依赖ES模块
        "target": "ES2022", // 至少需要ES2022支持类字段相关特性
        "decorators": "experimental", // TS 5.2+ 启用新版实验性装饰器
        // 其他原有配置不变
    }
}

注:TS 5.0-5.1版本需用experimentalDecorators: true+emitDecoratorMetadata: false来启用新版装饰器,但需确保项目未混用旧版语法。

2. 修正装饰器逻辑

你的新版代码逻辑与旧版不符:旧版是给装饰的字段绑定另一个方法的引用,而你写的代码在修改目标方法本身。正确的实现应该是给装饰的字段返回绑定后的方法:

const bound = <This, Method extends (...args: any[]) => any>(
  methodName: keyof This & string
) => {
  return function (
    _value: undefined,
    context: ClassFieldDecoratorContext<This, Method>
  ) {
    if (context.private) {
      throw new Error('Bound decorator cannot be used on private properties.');
    }

    // 返回字段初始化函数,赋值为绑定后的目标方法
    return function (this: This) {
      return (this[methodName] as Method).bind(this);
    };
  };
};

// 使用示例
class Foo {
  @bound('bar')
  foo!: () => string;

  bar(): string {
    return 'bar';
  }
}

// 测试
const fooInstance = new Foo();
console.log(fooInstance.foo()); // 输出 'bar'

3. 编译验证

修正后编译生成的JS会符合新版装饰器规范,上下文对象会正确提供addInitializer(如需使用初始化逻辑),或直接返回字段初始化函数,不会再触发原错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 21:07:26