打字稿:正确推断包装函数的参数和返回类型

我正在尝试创建一个包装函数,它接受一个查询和一个化简器,然后返回本质上相同的函数,但有一个附加属性 ( ) 包含_data查询数据的处理版本(具体来说,我想转换查询结果到Immutable.js记录)。

我希望此函数对任何可能的查询都是通用的,并保留查询中的调用签名和类型信息。我试图通过使用Parameters<typeof queryHook>和来做到这一点ReturnType<typeof queryHook>。但是,当我将参数传递给queryHook内部函数时,Typescript 抱怨说参数不能分配给内部函数,即使它们应该该函数的参数中键入。

我的问题是,为什么 Typescript 不能告诉传递给包装查询的参数应该与查询的预期参数具有相同的类型?

目前,它给出了一个错误,表明可能queryHook是任何可能的查询,而不是传递给函数的特定查询。

奇怪的是,如果我真的使用这个函数,所有类型都会被正确推断出来&hellip;&hellip;

我的代码如下:


/** List of all possible hooks to wrap */

type AllQueryHooks = typeof useGetPaymentsQuery | typeof useGetAddressesQuery


/**

 * @param queryHook GraphQL query to wrap

 * @param reducer Function to transform GraphQL result. Return value will be

 *     available on the _data prop

 */

export function wrapGQLHook<T, Hook extends AllQueryHooks>(

  queryHook: Hook,

  reducer: (data: ReturnType<typeof queryHook>['data']) => T,

): (

  ...args: Parameters<typeof queryHook>

) => ReturnType<typeof queryHook> & { _data: T | undefined } {

  function wrappedGQLHook(...args: Parameters<typeof queryHook>) {

    const queryResult = queryHook(...args)

    // ...args is overloaded      ^^^^^^^

    let _data: T | undefined = undefined

    if (queryResult.data) {

      _data = reducer(queryResult.data)

    }

    return { ...queryResult, _data }

  }

  return wrappedGQLHook

  //     ^^^^^^^^^^^^^^ not assignable to ReturnType<Hook>

}

/**

 * Wrapped useGetPaymentsQuery hook

 *

 * Returns useGetPaymentsQuery vars, plus a _data prop containing a List of

 * PaymentRecords

 */

export const useGetPaymentsQueryWrapped = wrapGQLHook(

  useGetPaymentsQuery,

  (data) => {

    if (!data) return

    const { Payments } = data

    return Payments.edges.map((node) => node)

    // ^^^^^^^^^^^^   return type inferred correctly &macr;\_(ツ)_/&macr;

  },

)


交互式爱情
浏览 92回答 1
1回答

慕仙森

与其尝试基于两个特定钩子的签名向后工作,不如尝试创建一个更通用的函数。Typescript 应该能够根据您作为参数传入的挂钩来推断特定类型。从思考我们知道什么和不知道什么开始。我们不知道的东西变成了泛型。钩子是一个带有一些参数(A)的函数。可以有任意数量的任意类型的参数 (&nbsp;A extends any[])。它返回一些对象 (&nbsp;R),该对象可能具有名为“数据”的属性 (&nbsp;R extends { data?: any })。reducer 是一个函数,它获取钩子 (&nbsp;R['data']) 返回的数据并将其映射到某个新值 (&nbsp;M)。修改后的钩子采用相同的参数 (&nbsp;A) 并返回一个对象,其中包含钩子的所有返回值 (&nbsp;R) 和一个附加属性“_data”,其值从 reducer 返回 (&nbsp;M) 或可能未定义 (&nbsp;R & { _data: M | undefined })。把所有这些放在一起,我们得到这个:export function wrapGQLHook<A extends any[], R extends { data?: any }, M>(&nbsp; &nbsp; queryHook: (...args: A) => R,&nbsp; &nbsp; reducer: (data: R['data']) => M,) {&nbsp; &nbsp; return (...args: A): R & { _data: M | undefined } => {&nbsp; &nbsp; &nbsp; &nbsp; const queryResult = queryHook(...args)&nbsp; &nbsp; &nbsp; &nbsp; let _data: M | undefined = undefined&nbsp; &nbsp; &nbsp; &nbsp; if (queryResult.data) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _data = reducer(queryResult.data)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return { ...queryResult, _data }&nbsp; &nbsp; }}这似乎对我有用,但在不知道钩子签名的情况下我无法非常彻底地测试它。游乐场链接
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript