Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
TypeScript 类型体操姿势合集 上有很多关于TS类型使用的挑战题目,对于题目的难度也分了几个级别,那就是热身、简单、中等、困难、地狱。
通过刷题学习,可以提升对TS类型的掌握能力。
热身
这个就跳过啦,真正的入门级。。
简单
实现 Pick
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
- 首先使用
extends来限制K的类型,这样可以确保使用的时候只能传入T的键类型 - 因为
K可能是联合类型,使用in遍历K,获取到每个键类型,再通过T[P]就可以获取对应的值类型啦
实现 Readonly
type MyReadonly<T> = {
readonly [P in keyof T]: T[P];
}
- 通过
in遍历T对象,然后组成新的类型对象,主要是在键前面添加个readonly属性就好了
实现Readonly的时候,没有要求进行递归处理,挑战中定义的类型,还特意在其中定义一个字面量对象类型,想必是想让挑战者自己去发现补充吧:
为了确保Todo1类型下嵌套的对象类型的属性也是readonly的,那么可以这么进行改进:
type MyReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? MyReadonly<T[P]> : T[P];
}
- 通过
extends进行条件判断,从而进行递归处理,这样确保嵌套的对象和数组都能变为readonly了
元组转换为对象
type TupleToObject<T extends readonly string[]> = {
[P in T[number]]: P
}
- 挑战中要求以下代码会报错:
// @ts-expect-error
type error = TupleToObject<[[1, 2], {}]>
所以我们要限定T的类型为string[]。
- 使用索引访问类型可以获取到元组中的类型。也就是通过
T[number]可以获取到联合类型:"tesla" | "model 3" | "model X" | "model Y"
索引访问类型使用文档:Indexed Access Types
- 后面就是常规操作啦,通过
in来迭代联合类型,然后进行组装了
第一个元素
type First<T extends any[]> = T[number] extends never ? never : T[0]
- 如果是空数组类型,那么
T[number]就会返回never类型的 - 而通过
T[0](索引类型访问)就可以获取到第一个元素的类型
在Issues看到一个👍比较多的答案,是通过infer来推断类型的:
type First<T extends any[]> = T extends [infer P, ...any[]] ? P : never
获取元组长度
type Length<T extends readonly any[]> = T extends { length: infer R } ? R : never;
- 通过条件约束加上
infer来推断长度的值类型,就可以准确返回数组的长度了 - 当然,数组得是不可变的,所以
T也要加上约束
在官方文档中,有这么一句话,说TS是允许推断出最长的返回类型,所以说肯定是知道数组的长度的:
There are a few interesting things to note in this example. We allowed TypeScript to infer the return type of longest. Return type inference also works on generic functions.