首页 > 解决方案 > 在 Typescript 中优雅地相交两种函数类型

问题描述

我有这两种功能类型..

wrapPageElement?(
  args: WrapPageElementBrowserArgs<DataType, PageContext, LocationState>,
  options: PluginOptions
): React.ReactElement

.. 和 ..

wrapPageElement?(
  args: WrapPageElementNodeArgs<DataSet, PageContext>,
  options: PluginOptions
): React.ReactElement

所以它们几乎相同,除了args. 差异对我的用例无关紧要。我可以完全依赖这两者之间的交叉类型。我写了以下类型:

type Params1 = Parameters<GatsbyBrowser['wrapPageElement']>
type Params2 = Parameters<GatsbySSR['wrapPageElement']>
type Return1 = ReturnType<GatsbyBrowser['wrapPageElement']>
type Return2 = ReturnType<GatsbySSR['wrapPageElement']>
type WrapPageElement = (args: Params1[0] | Params2[0], options: Params1[1] | Params2[1]) => Return1 | Return2;

这是一个最小的复制。这是可行的,但是是否可以WrapPageElement更优雅地编写类型或者这是要走的路?

Tl;博士

type PluginOptions = object;
type ReactElement = object;
type WrapPageElementBrowserArgs = {element: object, browserArgs: object};
type WrapPageElementNodeArgs = {element: object, nodeArgs: object};


type GatsbyBrowser = {
  wrapPageElement: (
    args: WrapPageElementBrowserArgs,
    options: PluginOptions
  ) => ReactElement
}

type GatsbySSR = {
  wrapPageElement: (
    args: WrapPageElementNodeArgs,
    options: PluginOptions
  ) => ReactElement
}

type Params1 = Parameters<GatsbyBrowser['wrapPageElement']>
type Params2 = Parameters<GatsbySSR['wrapPageElement']>
type Return1 = ReturnType<GatsbyBrowser['wrapPageElement']>
type Return2 = ReturnType<GatsbySSR['wrapPageElement']>
type WrapPageElement = (args: Params1[0] | Params2[0], options: Params1[1] | Params2[1]) => Return1 | Return2;

type WrapPageElement2 = GatsbyBrowser['wrapPageElement'] & GatsbySSR['wrapPageElement']; // not what I need

标签: typescript

解决方案


您正在寻找一种类型转换,它采用两种函数类型T并将U它们转换为单个函数类型,其中每个参数和返回类型是相应参数和来自and的返回类型的联合TU

我将这种转变称为UnionFunctions<T, U>需要一个更好的术语。您提到了函数的“相交”,但是 TypeScript 中的函数类型的交集相当于具有多个调用签名的函数的类型(即重载函数),并且与取每个参数的并集和返回不同类型。


无论如何,一种可能的方法是:

type UnionTuples<T extends any[], U extends { [K in keyof T]: any }> =
  { [K in keyof T]: T[K] | U[K] }    

type UnionFunctions<T extends (...args: any[]) => any, U extends (...args: any) => any> =
  (...args: UnionTuples<Parameters<T>, Parameters<U>>) => ReturnType<T> | ReturnType<U>

这里UnionTuples<T, U>需要两个元组TandU并产生一个元组,其中每个元素是来自 和 的相应元素的T并集UUnionTuples<[1, 2], [3, 4]>应该如此[1 | 3, 2 | 4]。这是一个简单的映射元组类型

然后UnionFunctionsUnionTuples在参数列表中使用Tand U(使用实用程序类型),返回类型是使用实用程序类型的返回类型Parameters<T>联合ReturnType<T>

定义它并不是非常冗长UnionFunctions,但是编写它比编写手动版本需要更多的字符。您需要UnionFunctions多次使用或使用长参数列表才值得。


让我们看看它是否适用于您的示例:

type WrapPageElement = UnionFunctions<
  GatsbyBrowser['wrapPageElement'], 
  GatsbySSR['wrapPageElement']
>
/* type WrapPageElement = (
     args: WrapPageElementBrowserArgs | WrapPageElementNodeArgs, 
     options: PluginOptions
 ) => ReactElement */

看起来挺好的!

Playground 代码链接


推荐阅读