为ramda添加类型

原创
10/01 17:13
阅读数 0

原文做了更多, 受限于自己水平不够...

https://medium.com/free-code-camp/typescript-curry-ramda-types-f747e99744ab

https://github.com/millsp/ts-toolbelt

ramda目前的实现是使用重载, 比如简单的add函数, 有两个重载

 

对于pipe这种类型, 暴力使用枚举写法一点都不够优雅, 并且还限制了参数个数

 

 

01 提取函数参数

const fn01 = (name: string, age: number, single: false) => true;

type Params<F extends (...args: any[]) => any> = F extends (
  ...args: infer A
) => any
  ? A
  : never;

type argsFn01 = Params<typeof fn01>;
// type argsFn01 = [name: string, age: number, single: false]

 

02 取第一个和取剩下的

head表示第一个, tail表示去除第一个后剩下的, 和ramda中的略有区别

type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
type h1 = Head<[string, number, boolean]>;
// type h1 = string
type h2 = Head<[]>;
// type h2 = never

type Tail<T extends any[]> = ((...t: T) => any) extends (
  _: any,
  ...tail: infer P
) => any
  ? P
  : [];

type t1 = Tail<[number, string, boolean]>;
// type t1 = [string, boolean]
type t2 = Tail<[boolean]>;
// type t2 = []
type t3 = Tail<[]>;
// type t3 = []

 

03 是否有剩余参数

type hasTail<T extends any[]> = T extends [] | [any] ? false : true;

type t4 = hasTail<[]>;
// type t4 = false

type t5 = hasTail<[string]>;
// type t5 = false

type t6 = hasTail<[string, number]>;
// type t6 = true

 

04 infer的几种用法

推断对象属性

type ObjectInfer<T> = T extends { K: infer P } ? P : never;

const obj1 = { K: "ace" };
const obj2 = { K: 1 };

type t1 = ObjectInfer<typeof obj1>;
// type t1 = string

type t2 = ObjectInfer<typeof obj2>;
// type t2 = number

type t3 = ObjectInfer<string>;
// type t3 = never

推断函数参数和返回值

type FunctionInfer<T> = T extends (...args: infer A) => infer R
  ? [A, R]
  : never;

const f05 = (name: string, age: number) => true;
type t5 = FunctionInfer<typeof f05>;
// type t5 = [[name: string, age: number], boolean]

推断promise

type PromiseInfer<T> = T extends Promise<infer P> ? P : never;
const p = new Promise<string>((resolve) => resolve("abc"));
type t06 = PromiseInfer<typeof p>
// type t06 = string

推断数组

// 一样的效果
// type ArrayInfer<T> = T extends Array<infer P> ? P : never;
type ArrayInfer<T> = T extends (infer P)[] ? P : never;
const a1 = [1, "a", true];
type t07 = ArrayInfer<typeof a1>;
// type t07 = string | number | boolean

推断元组

// type TupleInfer<T> = T extends [infer P, ...(infer Q)[]] ? [P, Q] : never;
type TupleInfer<T> = T extends [infer P, ...Array<infer Q>] ? [P, Q] : never;
type t08 = TupleInfer<[string, number, boolean]>;
// type t08 = [string, number | boolean]

 

05 单参数 版

type CurryV0<P extends any[], R> = (
  args: Head<P>
) => HasTail<P> extends true ? CurryV0<Tail<P>, R> : R;

declare function curryV0<P extends any[], R>(
  f: (...args: P) => R
): CurryV0<P, R>;

const f1 = (name: string, age: number, single: boolean) => true;
const curry1 = curryV0(f1);
const r1 = curry1("")(1)(true);
// const r1: boolean

curry1("")("");

 

 

06 多参数版, 但是推断有问题

使用可选参数, 但是与函数的参数消费不一致了, 所以返回值推断失效

type CurryV1<P extends any[], R> = (
  arg0: Head<P>,
  ...rest: Tail<Partial<P>>
) => HasTail<P> extends true ? CurryV1<Tail<P>, R> : R;

declare function CurryV1<P extends any[], R>(
  f: (...args: P) => R
): CurryV1<P, R>;

const f1 = (name: string, age: number, single: boolean) => true;
const curry1 = CurryV1(f1);
const r1 = curry1("")(1)(true);
// const r1: boolean

const r2 = curry1("", 1, true);
// const r2: CurryV1<[age: number, single: boolean], boolean>

 

 

07 其他的类型推断

取最后一个

type Last<T extends any[]> = {
  0: Last<Tail<T>>;
  1: Head<T>;
}[HasTail<T> extends true ? 0 : 1];

type t08 = Last<[string, number, boolean]>;
// type t08 = boolean
type t09 = Last<[]>;
// type t09 = never

元组或数组的长度

type Length<T extends any[]> = T["length"];
type t11 = Length<[]>;
// type t11 = 0

type t12 = Length<[number, string]>;
// type t12 = 2

// 如果使用变量会导致a04是数组, 这样长度就是会变化的,ts会返回nubmber
// const a04 = [1, "a", true];
type t13 = Length<[1, "a", true]>;
// type t13 = 3

在数组头部添加元素


type Prepend<V, T extends any[]> = ((head: V, ...args: T) => any) extends (
  ...args: infer U
) => any
  ? U
  : T;

type t14 = Prepend<1, ["a", "b"]>;
// type t14 = [1, "a", "b"]

type t15 = Prepend<number, [string, "a", "b", 2]>;
// type t15 = [number, string, "a", "b", 2]

去除列表中的前几项

使用递归, 每次向I中添加any, 并去除第一个元素, 当I的长度和N相等时, T已经去除了前N个了

type Drop<N extends Number, T extends any[], I extends any[] = []> = {
  0: Drop<N, Tail<T>, Prepend<any, I>>;
  1: T;
}[Length<I> extends N ? 1 : 0];
type t18 = Drop<2, [number, string, boolean, number]>;
// type t18 = [boolean, number]
type t19 = Drop<0, []>;
// type t19 = []
type t20 = Drop<3, [string]>;
// type t20 = []

转换函数

如果X继承Y, 则是X, 否则是Y

type Cast<X, Y> = X extends Y ? X : Y;

type t21 = Cast<string, any>;
// type t21 = string
type t22 = Cast<[string], any>;
// type t22 = [string];
type t23 = Cast<[number], string>;
// type t23 = string

 

08 

type CurryV3<P extends any[], R> = <T extends any[]>(
  ...args: T
) => Length<Drop<Length<T>, P>> extends 0 ? R : CurryV3<Drop<Length<T>, P>, R>;

由于Drop的返回有两个, 所以报错了, 但是我们知道, Drop由于递归的原因只可能返回一个类型, 此时需要一个类型转换函数

 

使用类型转换函数后只剩下一个可能无限循环的警告

type CastDrop<N extends Number, T extends any[]> = Cast<Drop<N, T>, any[]>;

type CurryV4<P extends any[], R> = <T extends any[]>(
  ...args: T
) => Length<CastDrop<Length<T>, P>> extends 0
  ? R
  : CurryV4<CastDrop<Length<T>, P>, R>;
 

 

 

有了准确的提示

type CastDrop<N extends Number, T extends any[]> = Cast<Drop<N, T>, any[]>;

type CurryV4<P extends any[], R> = <T extends any[]>(
  ...args: T
) => Length<CastDrop<Length<T>, P>> extends 0
  ? R
  : CurryV4<CastDrop<Length<T>, P>, R>;

declare function CurryV4<P extends any[], R>(
  f: (...args: P) => R
): CurryV4<P, R>;

const f1 = (name: string, age: number, single: boolean) => true;
const curry1 = CurryV4(f1);
const r1 = curry1("")(1)(true);
// const r1: boolean

const r2 = curry1("", 1, true);
// const r2: boolean
const r3 = curry1("", 1)(true);
// const r3: boolean

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部