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装饰器处理逻辑 } }
优势
- 签名更直观,开发者在不同环境下能看到精准的类型提示。
- 无需依赖条件类型,兼容性更强。
注意事项
- 配置提示:在库文档中明确告知开发者,使用遗留装饰器需开启
"experimentalDecorators": true,使用Stage 3装饰器需关闭该选项并确保TypeScript版本≥5.0。 - 运行时兼容性:必须通过参数个数或
context的特征(如kind属性)区分两种装饰器逻辑,避免运行时错误。 - 行为一致性:Stage 3装饰器通过返回值修改字段初始值,遗留装饰器通过修改
descriptor实现逻辑,需确保两种逻辑的最终行为一致。
内容的提问来源于stack exchange,提问作者Yoskutik
相关产品推荐
相关产品推荐

