如何声明 Currying 函数的类型?

开发 前端
我们需要获取参数列表的类型和函数的返回值类型。参数列表类型要包含参数的名称和参数的类型。那么如何获取函数类型的参数列表类型和返回值类型呢?

Challenge

在本次挑战中,您需要为 Currying 函数声明相应的类型,以帮助 TypeScript 编译器推断出正确的类型。

declare function Currying(fn: any): any

const curried1 = Currying((a: string, b: number, c: boolean) => true)
const curried2 = Currying((a: string, b: number, c: boolean, d: boolean, e: boolean, f: string, g: boolean) => true)
const curried3 = Currying(() => true)

type cases = [
  Expect<Equal<
    typeof curried1,
     (a: string) => (b: number) => (c: boolean) => true>>,
  Expect<Equal<
    typeof curried2,
     (a: string) => (b: number) => (c: boolean) => (d: boolean) => (e: boolean) => (f: string) => (g: boolean) => true
  >>,
  Expect<Equal<typeof curried3, () => true>>,
]

在上面的代码中,我们使用了两个工具类型 Expect 和 Equal,它们的实现代码如下:

type Expect<T extends true> = T
type Equal<X, Y> =
  (<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? true : false

Solution

首先,我们来分析一下第一个测试用例:

图片图片

由上图可知,我们需要获取参数列表的类型和函数的返回值类型。参数列表类型要包含参数的名称和参数的类型。那么如何获取函数类型的参数列表类型和返回值类型呢?这时我们可以使用 TypeScript 内置的 Parameters 和 ReturnType 工具类型。

type T0 = Parameters<() => true> // []
type T1 = Parameters<(a: string, b: number, c: boolean) => true>
// [a: string, b: number, c: boolean]

type T2 = ReturnType<() => void> // void
type T3 = ReturnType<(a: string, b: number, c: boolean) => true> // true

图片图片

在以上代码中,Parameters 工具类型用于获取函数类型的参数列表类型,它返回的是元组类型。而 ReturnType 工具类型则用于获取函数类型的返回值类型。它们的实现代码如下所示:

type Parameters<T extends (...args: any) => any> = 
  T extends (...args: infer P) => any ? P : never;
  
type ReturnType<T extends (...args: any) => any> = 
  T extends (...args: any) => infer R ? R : any;

在以上代码中,使用了 TypeScript 条件类型和 infer 类型推断。了解完以上代码,我们就知道如何获取函数类型的参数列表类型和返回值类型了。

接下来,我们要实现的功能就是使用函数的参数类型和返回值类型生成新的函数类型。下面我们来定义一个新的 ToCurrying 工具类型,它包含两个类型变量 Args 和 Return,分别表示参数的类型和返回值类型:

type ToCurrying<Args extends unknown[], Return> = unknown

然后,我们来继续分析第一个测试用例:

const curried1 = Currying((a: string, b: number, c: boolean) => true)

type cases = [
  Expect<Equal< typeof curried1,
    (a: string) => (b: number) => (c: boolean) => true>>
]

图片图片

参考以上的图片,我们可以总结出 ToCurrying 工具类型的处理流程:

图片图片

根据上述的处理流程,我们可以利用 TypeScript 条件类型、infer 类型推断和递归类型来实现对应的功能:

type ToCurrying<Args extends unknown[], Return> = 
  Args extends [...infer Head, infer Tail] 
   ? ToCurrying<Head, (arg: Tail) => Return> 
   : Return
   
type C0 = ToCurrying<[a: string, b: number, c: boolean], true>
// (arg: string) => (arg: number) => (arg: boolean) => true

type C1 = ToCurrying<[a: string, b: number, c: boolean, d: boolean, e: boolean, 
  f: string, g: boolean], true>
// (arg: string) => (arg: number) => (arg: boolean) => (arg: boolean) 
//   => (arg: boolean) => (arg: string) => (arg: boolean) => true

有了 ToCurrying 工具类型之后,我们来更新前面声明的 Currying 函数:

declare function Currying<T extends Function>(fn: T):
    T extends (...args: infer Args) => infer Return ?
    ToCurrying<Args, Return>
    : never
    
const curried1 = Currying((a: string, b: number, c: boolean) => true)
const curried2 = Currying((a: string, b: number, c: boolean, d: boolean, e: boolean, f: string, g: boolean) => true)
const curried3 = Currying(() => true)

type cases = [
  Expect<Equal<
    typeof curried1,
     (a: string) => (b: number) => (c: boolean) => true>>,
  Expect<Equal<
    typeof curried2,
     (a: string) => (b: number) => (c: boolean) => (d: boolean) => (e: boolean) 
       => (f: string) => (g: boolean) => true
  >>,
  Expect<Equal<typeof curried3, () => true>>,
]

更新后的 Currying 函数,已经可以满足前两个测试用例。但还不能满足最后一个测试用例:

图片图片

这是因为获取 () => true 函数类型的参数列表类型时,返回的是空元组类型,针对这种情形,我们需要进行对应的处理:

declare function Currying<T extends Function>(fn: T):
    T extends (...args: infer Args) => infer Return ?
    Args extends [] 
    ? () => Return
    : ToCurrying<Args, Return>
    : never

在以上代码中,当发现 Args 类型变量对应的类型是空元组类型的话,我们直接返回 () => Return 函数类型。之后,我们就通过了所有的测试用例。最后,我们来看一下完整的代码:

declare function Currying<T extends Function>(fn: T):
    T extends (...args: infer Args) => infer Return ?
    Args extends []
    ? () => Return
    : ToCurrying<Args, Return>
    : never

type ToCurrying<Args extends unknown[], Return> =
    Args extends [...infer Head, infer Tail]
    ? ToCurrying<Head, (arg: Tail) => Return>
    : Return
责任编辑:武晓燕 来源: 全栈修仙之路
相关推荐

2021-09-28 07:12:10

avaScriptCurrying柯里化

2010-05-21 17:14:18

MySQL 数字类型

2021-09-15 07:56:33

函数类型Go

2009-09-01 18:05:17

C#类型声明

2011-05-30 16:11:46

Javascript

2017-08-01 00:19:15

Javascript函数函数声明

2014-04-16 10:54:45

Javascript递归调用

2022-01-04 19:21:04

函数TypeScript重载

2023-03-20 08:14:11

PHP类型转换

2009-11-16 16:59:03

PHP构造函数

2010-02-22 16:51:03

Python 解析器

2009-08-20 10:34:46

C#中声明API函数

2009-09-01 10:49:28

C#具有隐式类型声明

2023-07-13 09:05:57

react hook类型types

2021-06-28 08:01:57

JS 函数表达式函数声明

2013-04-16 10:24:33

函数偏函数编程语言

2013-07-23 13:06:50

2021-01-14 10:10:51

Python函数Function An

2010-04-01 10:55:48

Oracle 数据类型

2024-01-17 06:23:35

SwiftTypeScript定义函数
点赞
收藏

51CTO技术栈公众号