【问题标题】:Generic TypeScript Type for functions returning functions that replaces the ReturnType with the ReturnType of the returned function返回函数的通用 TypeScript 类型,将 ReturnType 替换为返回函数的 ReturnType
【发布时间】:2019-02-10 12:48:55
【问题描述】:

亲爱的 TypeScript-3-Gurus,

有人可以帮我定义一个泛型类型GuruMagic<T> 吗?

T 是一个返回函数的函数,例如这个:

fetchUser(id: Id) => (dispatch: Dispatch) => Promise<boolean>

然后泛型类型应该替换fetchUserReturnType与返回函数的ReturnType。像这样:

type dispatchedAction = GuruMagic<typeof fetchUser>;
// so that dispatchedAction === (id: Id) => Promise<boolean>

我知道我可以申请ReturnType两次得到Promise&lt;boolean&gt;,但是我不知道如何将原始参数(可能是多个)与这个返回类型连接起来。 TypeScript (3.x) 甚至可以做到这一点吗?

为清楚起见的其他示例

const f1 = (a: number, b: string) => () => a;
type guruF1 = GuruMagic<typeof f1>; // (a: number, b: string) => number

const f2 = () => (name: string) => (otherName: string) => name + otherName;
type guruF2 = GuruMagic<typeof f2>; // () => (otherName: string) => string

动机

类型安全Redux-Thunk。当我 connect 一个 react 组件时,如果我能这样做会很棒:

import { fetchUser } from './myActions';

interface IPropsOfMyComponent {
  fetchUser: GuruMagic<typeof fetchUser>;
}

// ... MyComponent Definition ...

connect<{}, IPropsOfMyComponent, {}>(null, { fetchUser })(MyComponent)

【问题讨论】:

    标签: reactjs typescript redux typescript-typings redux-thunk


    【解决方案1】:

    是的,您可以使用 conditional types 和 TypeScript 3.0 中通过支持 tuple types in rest and spread expressions 引入的通用参数列表操作功能来做到这一点。这是一种方法:

    type GuruMagic<FF extends (...args: any[]) => (...args: any[]) => any> =
      FF extends (...args: infer A) => (...args: infer _) => infer R ? (...args: A) => R 
      : never;
    

    所以你推断函数FF的参数列表为A,函数FF的返回类型的返回类型为R,然后从参数列表中返回一个新函数AR。有用吗?

    declare function fetchUser(id: Id): (dispatch: Dispatch) => Promise<boolean>
    type dispatchedAction = GuruMagic<typeof fetchUser>;
    // (id: Id) => Promise<boolean>
    
    const f1 = (a: number, b: string) => () => a;
    type guruF1 = GuruMagic<typeof f1>; 
    // (a: number, b: string) => number
    
    const f2 = () => (name: string) => (otherName: string) => name + otherName;
    type guruF2 = GuruMagic<typeof f2>; 
    // () => (otherName: string) => string
    

    是的!

    我不能 100% 确定您将如何在实践中使用此类型函数……事实上它忽略了中间函数的参数类型,这让我很担心。如果您尝试在给定T 类型的函数的情况下实际实现GuruMagic&lt;T&gt; 类型的函数,除非您以某种方式提供缺少的参数,否则您将遇到问题。但也许你甚至没有在给定T 的情况下实现GuruMagic&lt;T&gt;。无论如何,这是你的事,不是我的。

    无论如何,希望对您有所帮助。祝你好运!

    【讨论】:

    • 感谢您的精彩回答!完美运行!我只会将它用于 redux-thunk-actions。我忽略了中间参数,但在调度操作时由 redux 本身使用。当您将对象作为mapDispatchToProps 参数传递时,如我的动机代码块的最后一行所示,redux 基本上将其转换为{ fetchUser: (...args) =&gt; dispatch(fetchUser(...args)) }。因此,您的 GuruMagic 类型让我可以推断出 this 的结果类型,以便更轻松地键入我的组件道具。再次感谢! :) PS:我可能会调整你的定义,只允许ThunkActions
    • 完美答案!不过我很好奇,有没有办法修改它以使用函数对象?
    • @PeterKottas “函数对象”是什么意思?所需的输入和输出类型是什么?可能您只想使用mapped type。如果您的问题更复杂,或者您需要的帮助比您自己的问题要多。
    • @jcalz 与此处提到的示例完全相同,我只有值是函数的对象。我想在那个对象上使用这个 GuruMagic 而不是单个函数。这个想法是它会为您提供该对象的类型,从而减少该对象中所有函数的复制粘贴代码。 (你也可能忘记映射一个函数)
    • 实际上这可能有效(afk)type GuruObjectMagic&lt;T&gt; = {[P in keyof T]?: GuruMagic&lt;typeof T[P]&gt;;}
    猜你喜欢
    • 2020-08-31
    • 2018-10-23
    • 1970-01-01
    • 2022-11-04
    • 2022-01-10
    • 2019-01-15
    • 2020-05-30
    • 1970-01-01
    相关资源
    最近更新 更多