typescript - 在 typescript 中使用回调来 promise 函数

标签 typescript

我有一个类在接受回调的方法上有多个重载。 我想让它们基于Promise,但是我在使用 TypeScript 泛型以类型安全的方式执行此操作时遇到了麻烦。

基本上 API 是这样的:

class CallbackClient {
  method(req: Request, callback: (error: Error, response: Response) => void): void
  method(req: Request, options: Options, callback: (error: Error, response: Response) => void): void
}

应该变成这样:

class PromiseClient {
  method(req: Request): Promise<Response>
  method(req: Request, options: Options): Promise<Response>
}

但是如何在 TypeScript 中将 PromiseClient 定义为通用类型? 在伪代码中,我想要这样的类型:

type PromiseClient<T> = {
  [P in keyof T]: (args of T[P] without the callback) => Promise<response of T[P] in callback>
}

我有一个实现可以做到这一点,但由于我无法实现这一点,我失去了类型安全。

映射它的函数是这样的:

function toPromiseClient<T extends CallbackClient>(client: T): PromiseClient<T> {
  const promiseClient: PromiseClient<T> = {}
  Object.keys(Object.getPrototypeOf(client))
    .forEach((functionName: string) => {
      const originalFunction = client[functionName as keyof T]
      if (!originalFunction) {
        return
      }
      promiseClient[functionName as keyof T] = (...args: any[]) => new Promise<any>((resolve, reject) => {
        if (args && args.length > 1 && typeof (args[args.length - 1]) === 'function') {
          reject(new Error('Use Promise API instead of callbacks'))
          return
        }
        originalFunction.call(client, ...args, (error: Error, res: any) => {
          if (error) {
            reject(error)
            return
          }
          resolve(res)
        })
      })
    })
  return promiseClient
}

最佳答案

这在 typescript 2.8 中是部分可行的(尚未发布,应该在 2018 年 3 月发布,你可以通过 npm install -g typescript@next 获取)

解决方法:

declare class CallbackClient {
    method1(req: Request, options: Options, callback: (error: Error, response: Response) => void): void
    method1(req: Request, callback: (error: Error, response: Response) => void): void


    method2(req: Request, options: Options, callback: (error: Error, response: Response) => void): void

    method3(req: Request, options?: Options, callback?: (error: Error, response: Response) => void): void
}

type Promisify<T> = {
    [P in keyof T]:
    T[P] extends (callback: (error: Error, response: infer TResponse) => void) => void ? () => Promise<TResponse> :
    T[P] extends (p1: infer P1, callback: (error: Error, response: infer TResponse) => void) => void ? (p1: P1) => Promise<TResponse> :
    T[P] extends (p1: infer P1, p2: infer P2, callback: (error: Error, response: infer TResponse) => void) => void ? (p1: P1, p2: P2) => Promise<TResponse> :
    T[P] extends (p1: infer P1, p2: infer P2, p3: infer P3, callback: (error: Error, response: infer TResponse) => void) => void ? (p1: P1, p2: P2, p3: P3) => Promise<TResponse> :
    never;
}

type PromiseClient = Promisify<CallbackClient>; 
// Will be equivalent to :
// type PromiseClient = {
//     method1: (p1: Request) => Promise<Response>; // Only one overload, not the second
//     method2: (p1: Request, p2: Options) => Promise<Response>;
//     method3: (p1: Request, p2: Options | undefined) => Promise<Response>;
// }

上面的解决方案使用条件类型从原始类的每个方法中提取参数类型,然后使用这些类型为返回 promise 的方法创建新的函数类型。

限制:

  1. 它不适用于任意数量的参数,上述解决方案最多适用于 3 个参数和回调,但您可以添加更多
  2. 参数名丢失
  3. 如果参数是可选的,它们将成为必需的,其类型为 ArgType | undefined
  4. 如果一个方法有多个重载,只有参数最少的那个才会进入结果。在上面的例子中,我们只得到了method1: (p1: Request) => Promise<Response>我们也没有得到 method1: (p1: Request, p2: Options) => Promise<Response> (根据重载的顺序,您可能会得到奇怪的结果,这很古怪)。

关于typescript - 在 typescript 中使用回调来 promise 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49303695/

相关文章:

arrays - 如何在 TypeScript 中向对象数组添加具有不同值的新属性?

node.js - 如何在 Visual Studio Code (VS Code) 上调试用 Typescript 编写的 Express 应用程序

typescript - 可索引的部分

javascript - 如何 console.log() 一个 Angular 项目?

typescript - 类 'NeedAuthGuard' 错误地实现了类 'CanActivate'。您的意思是扩展 'CanActivate' 并将其成员作为子类继承吗?

typescript - Jest 和 TypeScript 的不完整 stub

javascript - Angular 7 项目无法导入 node-vibrant

typescript - 检查接口(interface)是否有必填字段

angular - 类型 void 不可分配给类型 any

reactjs - 需要帮助理解这种奇怪的 Jest 行为