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

如何编写兼容类方法与字段函数的TypeScript 5类型安全装饰器工厂

实现兼容类方法与字段函数的TypeScript 5类型安全装饰器工厂

要解决这个问题,核心是利用函数重载明确区分方法装饰器与字段装饰器的签名差异,同时在实现中分别处理两种场景的逻辑。以下是完整的类型安全解决方案:

完整代码实现

import { TypedPropertyDescriptor } from "typescript";

// 重载1:适配类方法(含实例方法、静态方法)
function customLogger<T extends (...args: any[]) => Promise<any>>(
  target: any,
  propertyKey: string | symbol,
  descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> | void;

// 重载2:适配类字段函数(含实例字段、静态字段)
function customLogger<T extends (...args: any[]) => Promise<any>>(
  target: any,
  propertyKey: string | symbol
): (initialValue: T) => T;

// 通用实现逻辑
function customLogger(
  target: any,
  propertyKey: string | symbol,
  descriptor?: PropertyDescriptor
) {
  // 场景1:处理类方法(存在descriptor参数)
  if (descriptor) {
    const originalMethod = descriptor.value as (...args: any[]) => Promise<any>;
    descriptor.value = async function (...args: any[]) {
      console.log(`[LOG] 调用方法 ${String(propertyKey)},参数:`, args);
      try {
        const result = await originalMethod.apply(this, args);
        console.log(`[LOG] 方法 ${String(propertyKey)} 执行成功,结果:`, result);
        return result;
      } catch (error) {
        console.error(`[LOG] 方法 ${String(propertyKey)} 执行失败,错误:`, error);
        throw error;
      }
    };
    return descriptor;
  } 
  // 场景2:处理类字段函数(无descriptor参数)
  else {
    return function (initialValue: (...args: any[]) => Promise<any>) {
      return async function (...args: any[]) {
        console.log(`[LOG] 调用字段函数 ${String(propertyKey)},参数:`, args);
        try {
          const result = await initialValue.apply(this, args);
          console.log(`[LOG] 字段函数 ${String(propertyKey)} 执行成功,结果:`, result);
          return result;
        } catch (error) {
          console.error(`[LOG] 字段函数 ${String(propertyKey)} 执行失败,错误:`, error);
          throw error;
        }
      };
    };
  }
}

使用示例

class DemoService {
  // 实例字段函数
  @customLogger
  asyncField = async (input: number): Promise<number> => {
    return input * 3;
  };

  // 实例方法
  @customLogger
  async asyncMethod(input: string): Promise<string> {
    return `处理结果:${input}`;
  }

  // 静态字段函数
  @customLogger
  static staticAsyncField = async (msg: string): Promise<string> => {
    return `静态字段:${msg}`;
  };

  // 静态方法
  @customLogger
  static async staticAsyncMethod(msg: string): Promise<string> {
    return `静态方法:${msg}`;
  }
}

关键细节说明

  1. 重载区分场景

    • 方法装饰器的签名包含descriptor参数,返回值为TypedPropertyDescriptor<T> | void,用于修改方法的属性描述符
    • 字段装饰器无descriptor参数,返回值为一个初始化函数,用于在字段赋值时包装原函数
  2. 类型安全保障

    • 通过泛型T extends (...args: any[]) => Promise<any>约束装饰器仅作用于异步函数
    • 保留原函数的this绑定(使用普通函数而非箭头函数),确保原函数的上下文行为不变
  3. TypeScript配置要求
    需在tsconfig.json中启用装饰器相关配置:

    {
      "compilerOptions": {
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "target": "ES2022" // 或更高版本,支持异步函数语法
      }
    }
    

内容的提问来源于stack exchange,提问作者Jialiang T.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 23:12:35