小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
序言:
这是搞定 TS 第九篇,如果没有基础的小伙伴想要从零搞定 TS ,请从第一篇开始juejin.cn/post/701033…
第一题
实现一个 OptionalKeys 工具类型,用来获取对象类型中声明的可选属性。具体的使用示例如下所示:
type Person = {
id: string;
name: string;
age: number;
from?: string;
speak?: string;
};
type OptionalKeys<T> = // 你的实现代码
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"
该题考察内容
- 可选属性的特征为指定属性 | undefined。所以我们从 undefined 出发即可。
- 筛选出符合条件的,将属性名赋值给类型名字,不符合的类型赋值为never,方便更改完成后取出的联合类型为结果。
- Exclude<T, U>。 将特定属性排除。
题解
type Person = {
id: string;
name: string;
age: number;
from?: string;
speak?: string;
};
type OptionalKeys<T> = Exclude<{
[P in keyof T ] : undefined extends T[P] ? P: never
}[keyof T] ,undefined>
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"
第二题
实现一个 Curry 工具类型,用来实现函数类型的柯里化处理。具体的使用示例如下所示:
type Curry<
F extends (...args: any[]) => any,
P extends any[] = Parameters<F>,
R = ReturnType<F>
> = // 你的实现代码
type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; // (arg_0: number) => (b: string) => Date
本题考察知识点
解题思路
柯里化的结束条件是参数只有一个的时候开始运算,所以我们把这个作为结束递归的条件
type Curry<
F extends (...args: any[]) => any,
P extends any[] = Parameters<F>,
R = ReturnType<F>
> = P extends [first: infer A ,...rest: infer B ]
? B["length"] extends 0
? F :(arg:A)=>Curry<(...args: B)=> R
>: F
type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; // (arg_0: number) => (b: string) => Date
小结: 我们看上述步骤,首先我们将参数使用 infer 分离开了,然后判断参数数量是否是一个,是一个就开始执行,否则将剩余参数作为入参进行递归。