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

TypeScript兼容新旧装饰器:如何判断开发者使用的版本?

实现兼容TypeScript遗留装饰器与Stage 3装饰器的NPM库类型定义

TypeScript目前存在两种装饰器类型:遗留(legacy)装饰器与Stage 3装饰器,示例类型如下:

// 遗留装饰器类型
const legacyDecorator: PropertyDecorator;

// Stage 3装饰器类型
const modernDecorator: <This, Value>(value: unknown, context: ClassFieldDecoratorContext<This, Value>) => void;

开发NPM库时需要同时兼容这两种装饰器,但无法直接判断开发者使用的装饰器版本,希望通过类似ChooseType的类型定义,为新装饰器提供具体类型支持的同时兼容遗留装饰器。


方案1:基于TypeScript环境的条件类型自动适配

利用TypeScript的条件类型,通过检查ClassFieldDecoratorContext是否存在(该类型仅在Stage 3装饰器环境中可用),自动选择对应的装饰器类型:

// 判断当前环境是否启用Stage 3装饰器
type IsStage3DecoratorEnv = typeof ClassFieldDecoratorContext extends undefined ? false : true;

// 兼容两种装饰器的通用类型
type CompatiblePropertyDecorator = IsStage3DecoratorEnv extends true
  ? <This, Value>(value: unknown, context: ClassFieldDecoratorContext<This, Value>) => void | Value
  : PropertyDecorator;

// 定义兼容装饰器
const myDecorator: CompatiblePropertyDecorator = (...args) => {
  // 运行时区分逻辑:遗留装饰器接收3个参数(target, propertyKey, descriptor),Stage3接收2个(value, context)
  if (args.length === 3) {
    // 处理遗留装饰器逻辑
    const [target, propertyKey] = args;
    Object.defineProperty(target, propertyKey, {
      get() {
        return `legacy: ${this['_' + propertyKey]}`;
      },
      set(val) {
        this['_' + propertyKey] = val;
      }
    });
  } else {
    // 处理Stage3装饰器逻辑
    const [value, context] = args;
    if (context.kind === 'field') {
      return `stage3: ${value}`;
    }
  }
};

原理说明

  • 当开发者启用Stage 3装饰器(tsconfig.json中设置"experimentalDecorators": false,TypeScript版本≥5.0),ClassFieldDecoratorContext会被定义,条件类型自动切换为Stage 3装饰器类型。
  • 当使用遗留装饰器("experimentalDecorators": true),ClassFieldDecoratorContext不存在,条件类型自动匹配PropertyDecorator。

方案2:函数重载显式支持两种签名

通过函数重载直接定义两种装饰器的签名,TypeScript会根据开发者的装饰器环境自动匹配对应的重载:

// 重载1:Stage3装饰器签名
function myDecorator<This, Value>(value: unknown, context: ClassFieldDecoratorContext<This, Value>): void | Value;
// 重载2:遗留装饰器签名
function myDecorator(target: any, propertyKey: string | symbol, descriptor?: PropertyDescriptor): void;
// 实现函数
function myDecorator(...args: any[]) {
  // 运行时判断逻辑同方案1
  if (args.length === 3) {
    const [target, propertyKey] = args;
    // 遗留装饰器处理逻辑
  } else {
    const [value, context] = args;
    // Stage3装饰器处理逻辑
  }
}

优势

  • 签名更直观,开发者在不同环境下能看到精准的类型提示。
  • 无需依赖条件类型,兼容性更强。

注意事项

  1. 配置提示:在库文档中明确告知开发者,使用遗留装饰器需开启"experimentalDecorators": true,使用Stage 3装饰器需关闭该选项并确保TypeScript版本≥5.0。
  2. 运行时兼容性:必须通过参数个数或context的特征(如kind属性)区分两种装饰器逻辑,避免运行时错误。
  3. 行为一致性:Stage 3装饰器通过返回值修改字段初始值,遗留装饰器通过修改descriptor实现逻辑,需确保两种逻辑的最终行为一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 17:07:13