如何在返回其回调之一结果的函数的 Typescript 中声明类型?

Posted

技术标签:

【中文标题】如何在返回其回调之一结果的函数的 Typescript 中声明类型?【英文标题】:How do I declare typings in Typescript of a function which returns a result of one of its callbacks? 【发布时间】:2021-08-08 18:38:44 【问题描述】:

我有一个mapResponse 函数,它接受另一个函数(调用者)和一个映射器函数。函数的结果应该是第三个函数,它是提供的回调的组合:它应该接受调用者的参数,但返回映射器的结果。

调用者可能会产生 Promise,所以应该在 mapper 获取他的参数之前将 Promise 解包。

所以我的问题不是实现(已经完成)而是打字。如何使用 Typescript 声明来描述这种行为?

export function mapResponse<
  Caller extends (...args: any[]) => unknown,
  Mapper extends <R>(response: Unpacked<ReturnType<Caller>>) => R
>(
  caller: Caller,
  mapper: Mapper
): (...args: Parameters<Caller>) => ReturnType<Mapper> 
  return (...args: Parameters<Caller>) => 
    const res = caller(...args) as Unpacked<ReturnType<Caller>>
    if (res instanceof Promise) 
      return res.then(mapper) as ReturnType<Mapper>
    
    return mapper(res)
  

如果有人需要,这里是Unpacked 声明:

export type Unpacked<T> = T extends (infer U)[]
  ? U : T extends (...args: any[]) => infer U
  ? U : T extends Promise<infer U>
  ? U : T

【问题讨论】:

【参考方案1】:

我将声明函数如下:

type Unpacked<T> = T extends Promise<infer U> ? U : T;

declare function mapResponse
    <
        TCaller extends (...args: any[]) => any,
        TMapper extends (input: Unpacked<ReturnType<TCaller>>) => any
    >(caller: TCaller, mapper: TMapper):
        TMapper extends (input: Unpacked<ReturnType<TCaller>>) => infer R ? (...args: Parameters<TCaller>) => R : never;


const func1 = mapResponse((a: number, b: number) => a + b, sum => sum.toString()); // (a: number, b: number) => string
const func2 = mapResponse((a: number, b: number) => Promise.resolve(a + b), sum => sum.toString()); // (a: number, b: number) => string

与您的示例相比,不同之处在于 TMapper 必须在右侧也有 extends 定义,才能正确地 infer R

TypeScript 操场示例在这里:https://tsplay.dev/wOav6m

【讨论】:

以上是关于如何在返回其回调之一结果的函数的 Typescript 中声明类型?的主要内容,如果未能解决你的问题,请参考以下文章

如何等到回调被调用

如何等到回调被调用

缓存其参数返回值的函数

如何在回调后将值返回给main函数

如何使用返回数据的回调函数设置匿名块

在多个函数中重用一个rxjs'主题