TypeScript类型体操挑战(一)

478 阅读3分钟

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
}
  1. 挑战中要求以下代码会报错:
// @ts-expect-error
type error = TupleToObject<[[1, 2], {}]>

所以我们要限定T的类型为string[]

  1. 使用索引访问类型可以获取到元组中的类型。也就是通过T[number]可以获取到联合类型:"tesla" | "model 3" | "model X" | "model Y"

索引访问类型使用文档:Indexed Access Types

  1. 后面就是常规操作啦,通过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.