【问题标题】:How to infer function return type based on an function array argument?如何根据函数数组参数推断函数返回类型?
【发布时间】:2021-07-18 05:20:54
【问题描述】:

给定一个普通的 JS 函数,该函数接受一个回调数组(每个都返回一个对象),然后返回它们的组合输出:

export const applyCallback = (callbacks) => {
  return callbacks.reduce((acc, callback) => { ...acc, ...callback() }, {});
}

如何使用 TypeScript 适当地键入这个来推断 applyCallback 的返回类型?虽然我知道ReturnType,但它适用于单个函数,同时在这里我想获得每个回调的ReturnType 并创建它们的返回类型的联合。

【问题讨论】:

  • 我猜每个回调都可以返回一个不同的对象。对吗?
  • @RamanNikitsenka 是的,没错!

标签: typescript types


【解决方案1】:

从captain-yossarian 的answer 解决“在这种情况下类型断言没有帮助”(否则完全没问题):没有必要返回对象的交集并且完全可以构建所需的在 as 断言的帮助下创建对象。

更重要的是,如果返回的类型只具有 optional 属性,则可以完全避免断言(但由于reduce 方法签名的类型,这几乎是不可能的以确保编译器可以安全地从空对象跳转到完全填充的对象)。

借助一些key remapping 和映射类型,任务变得相当简单0。

type AnyFunc = (...args:any[]) => any;

function applyCallback<T extends AnyFunc[]>(callbacks: T) {
  return callbacks.reduce((acc, callback) => {
    return { ...acc, ...callback() }
  }, {} as { [ P in ReturnType<T[number]> as keyof P ] : P[keyof P] });
}


const result = applyCallback([
  () => ({ age: 42 }),
  () => ({ name: 'John' })
]); /* { age: number; name: string; } */

Playground


0 请注意,applyCallback 签名遵循问题中接受单个函数数组而不是其余参数的签名。

【讨论】:

    【解决方案2】:

    可以通过一些重载来完成:

    
    type Fn = () => object
    
    // credits goes to https://stackoverflow.com/a/50375286
    type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
      k: infer I
    ) => void
      ? I
      : never;
    
    
    function applyCallback<R extends Fn, U extends ReadonlyArray<R>>(...callbacks: U): UnionToIntersection<ReturnType<U[number]>>;
    function applyCallback(...callbacks: Fn[]) {
      return callbacks.reduce((acc, callback) => {
        return { ...acc, ...callback() }
      }, {});
    }
    
    
    const result = applyCallback(
      () => ({ age: 42 }),
      () => ({ name: 'John' })
    ); // {age:42} & {name:'John'}
    

    Playground

    TypeScript 很难缩小 reduce 的初始值。大多数类型的人都在使用类型断言as,但是在这种情况下类型断言没有帮助,所以我决定使用重载。

    您可以在我的blog 中找到有关键入回调的更多信息

    【讨论】:

    • 这太完美了,谢谢!感谢您也链接到另一个 SO 线程,因为它引导我访问 Typescript 的相关文档以了解发生了什么。
    • catchts.com/callbacks 在这里您可以找到更多信息
    猜你喜欢
    • 1970-01-01
    • 2020-06-06
    • 1970-01-01
    • 2020-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-23
    • 1970-01-01
    相关资源
    最近更新 更多