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

Typescript仅提取类可调用成员类型并实现带上下文的命令模式

解决方案

核心思路

通过TS泛型工具过滤类中的非可调用成员,定义强类型的命令结构绑定实例上下文,同时完整透传方法的参数与返回值类型,满足类型校验、上下文保留、返回值透传三个核心需求。

实现步骤

1. 定义类型工具提取类的可调用成员

该工具会自动过滤类中的普通属性,仅保留方法类型的键与对应函数签名:

type ExtractMethods<T> = {
  [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: T[K]
}

2. 定义强类型命令结构

命令结构绑定实例、方法名、参数,同时预定义带正确返回值的执行方法:

type ClassCommand<T, M extends keyof ExtractMethods<T>> = {
  instance: T
  methodName: M
  args: Parameters<ExtractMethods<T>[M]>
  execute: () => ReturnType<ExtractMethods<T>[M]>
}

3. 实现命令创建工厂函数

工厂函数会自动做类型校验,仅允许传入类的合法方法名,同时绑定实例上下文保证this指向正确:

function createCommand<T, M extends keyof ExtractMethods<T>>(
  instance: T,
  methodName: M,
  ...args: Parameters<ExtractMethods<T>[M]>
): ClassCommand<T, M> {
  return {
    instance,
    methodName,
    args,
    // 箭头函数绑定实例上下文,避免this丢失
    execute: () => instance[methodName](...args)
  }
}

4. 实现保留返回值的重试包装函数

泛型定义的重试函数会自动继承命令执行方法的返回值类型,不会出现类型丢失:

async function callWithRetry<R>(
  command: { execute: () => R },
  retryTimes = 3
): Promise<Awaited<R>> {
  let lastError: Error
  for (let i = 0; i < retryTimes; i++) {
    try {
      // 自动适配同步/异步方法返回值
      return await Promise.resolve(command.execute())
    } catch (e) {
      lastError = e as Error
      // 指数退避等待,可按需调整
      await new Promise(resolve => setTimeout(resolve, 100 * Math.pow(2, i)))
    }
  }
  throw lastError!
}

效果验证

用包含普通属性的示例类测试:

class MyClass {
  // 普通非可调用属性
  public version = '1.0.0'
  public count = 0

  // 可调用方法
  add(a: number, b: number): number {
    this.count += 1
    return a + b
  }

  async fetchData(id: string): Promise<{ id: string; value: number }> {
    this.count += 1
    return { id, value: Math.random() }
  }
}

const inst = new MyClass()

// 合法场景:创建add方法命令
const addCmd = createCommand(inst, 'add', 1, 2)
console.log(addCmd.execute()) // 输出3,count变为1,this指向正确,返回值类型为number

// 合法场景:命令传入重试函数,返回值类型完整保留
const fetchCmd = createCommand(inst, 'fetchData', 'test-id')
// fetchRes类型为 { id: string; value: number }
const fetchRes = await callWithRetry(fetchCmd)

// 非法场景1:选择普通属性作为方法,TS直接抛出类型错误
// @ts-expect-error 类型"version"无法赋值给类型"add" | "fetchData"
const invalidCmd = createCommand(inst, 'version')

// 非法场景2:参数不匹配触发类型校验
// @ts-expect-error 类型"string"无法赋值给类型"number"
const wrongArgsCmd = createCommand(inst, 'add', '1', 2)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 02:06:02