原文:How to master advanced TypeScript patterns
image.png

Learn how to create types for curry and Ramda

  1. const toCurry = (name:string, age:number,single: boolean) => true
  2. const curried = curry(toCurry);
  3. const test0 = curried('Jane')('26', true);
  4. const test1 = curried(R._, 26)('Jane', R._)(R._)([true])

What is currying?

柯里化是指把一个带有多个参数的函数转成每次执行都只有一个参数的过程。如下所示:

  1. const sampleAdd = (a: number, y: number) => x + y;
  2. const test00 = sampleAdd(4, 6) // 10

sampleAdd的柯里化版本如下:

  1. const curriedAdd = (x: number) => (y: number) => x + y;
  2. const test01 = curriedAdd(4)(2); // 6
  3. const test02 = curriedAdd(15); // Function
  4. const test03 = test02(5); // 20

Tuple types

Tuple允许你定义个一个数组,并具有固定数量/类型的元素。

  1. type tuple = ['a', number, string[]];

它们可用于在固定大小的数组中强制这种类型的值。

  1. const test04: tuple = ['a', 1, ['b', 'c']];

也可以用于剩余参数的联合或者解构:

  1. const test05 = (...args: tuple) => true
  2. const test06 = test05('a', 42, []);

一个基础的函数:

  1. const fn00 = (name: string, age: number, single: boolean) => true;
  2. const test07 = Parameters<typeof fn00> // [string, number, boolean]

感谢Parameters的魔法,我们可以从fn00提取参数类型。
一个更复杂的例子:

  1. type Params<F extends (...args: any[]) => any> =
  2. F extends ((...args: infer A) => any)
  3. ? A
  4. : never

验证下:

  1. type test08 = Params<typeof fn00> //[string, number, boolean]

这个和Parameters的执行结果是一样的。

Head

Head通过获取tuple 类型 T,并且返回tuple里面的第一个类型

  1. type Head<T extends any[]> =
  2. T extends [any, ...any[]]
  3. ? T[0]
  4. : never

验证下:

  1. type test9 = Head<[1, 2, string, number]>; // 1
  2. type test10 = Head<Params<typeof fn00>>; // string

Tail

一个经典的柯里化函数通过一个接一个的消费参数。这意味着当我们消费Head<Params<F>>,一般情况下 我们还要消费下一个参数在函数还没有调用完成时。这就是Tail的目的,他可以轻松的删除掉tuple中的第一个实体。

  1. type Tail<T extends any[]> =
  2. ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any)
  3. ? TT
  4. : []

测试一下:

  1. type test11 = Tail<[1, 2,string,number]> // [2, string, number]
  2. type test12 = Tail<Params<typeof fn00>> // [number, boolean]
  3. type test13 = Tail<test12> // [boolean]

HasTail

一个柯里化函数直到消费完所有的参数后就返回一个函数。这种情况我们把它叫做Tail,直到没有参数可以被消费

  1. type HasTail<T extends any[]> =
  2. T extends ([] | [any])
  3. ? false
  4. : true

测试一下:

  1. type params = [string, number];
  2. type test14 = HasTail<params>;
  3. type test15 = HasTail<Tail<params>>
  4. type test16 = HasTail<Tail<Tail<params>>>

Important keywords

有三个重要的关键字:typeextendsinfer

  • extends:
  • type
  • infer

Extract a property’s type from an object

从一个对象中提取属性的类型

  1. type Objectinfer<O> =
  2. O extends {a: infer A}
  3. ? A // if true
  4. : never // if false

测试一下:

  1. const object = {a: 'hello'};
  2. const test17 = Objectinfer<typeof object>; // string
  3. const test18 = Objectinfer<string>; // never

Extract inner types from function types

从函数类型里提取类型。

  1. type FunctionInfer<F> =
  2. F extends (...args: infer A) => infer R
  3. ? [A: R] // if true
  4. : never // if false

测试一下

  1. const fn01 = (a: number, b: any) => any
  2. type test19 = FunctionInfer<typeof fn01> // [[number, any],any]

Extract generic types from a class or an interface

从一个类或者接口中提取泛型类型

  1. type ClassInfer<I> =
  2. I extends Promise<infer G>
  3. ? G // if true
  4. : never // if false

测试一下

  1. const promise = new Promise<string>(() => {})
  2. type test20 = ClassInfer<typeof promise> //string

Extract types from an array

从数组中提取类型

  1. type ArrayInfer<> =
  2. T extends (infer U) []
  3. ? U // if true
  4. : never // if false

测试一下:

  1. const array = [0, 'data', 1, 'data'];
  2. type test21 = ArrayInfer<typeof array> // string | number

Extract types from a tuple

tuple中提取类型

  1. type TupleInfer <T> =
  2. T extends [infer A ,...(infer B)[]]
  3. ? [A, B]
  4. : never

测试一下:

  1. type test22 = TupleInfer<[string, number, boolean]>
  2. // [string, number | boolean]