typescript 类型体操 之 14-easy-first

302 阅读4分钟

「这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战

前言

在学习typescript的过程当中,有一个github库对其类型的学习特别有帮助,是一个有点类似于leetcode的刷题项目,能够在里面刷各种关于typescript类型的题目,本片文章带大家做其中简单难度的第四道题 14-easy-firs

type-challenges/type-challenges: Collection of TypeScript type challenges with online judge (github.com)

14-easy-firs

首先还是先来看一下题目的README来看一下题目的要求

image.png

type cases = [
  Expect<Equal<First<[3, 2, 1]>, 3>>,
  Expect<Equal<First<[() => 123, { a: string }]>, () => 123>>,
  Expect<Equal<First<[]>, never>>,
  Expect<Equal<First<[undefined]>, undefined>>
]

结合题目给的README和测试case,我们能发现这次要实现的函数为 First<T>,接收一个数组并且返回第一个元素。

在测试中,我们能看到,进入数组为 [3,2,1] 则返回一个 3,并且还能返回一个函数,注意在空数组的情况下将会返回一个 never

接下来我们就开始做题

利用js进行对比学习

首先我们还是先用js来模拟题目的要求。

这次题目的要求用js来模拟就非常简单,我们接收一个数组,并且返回数组的第一项,要注意当数组为空的时候返回 never

// js
const first = (arr) => {
    if(arr.length == 0) return "never"
    return arr[0];
};

实现 First

接着我们就按照js的逻辑来实现ts的代码

  1. 将传入数组的第一项返回出去:
type First<T extends any[]> = T[0]

意料之中测试的never出错了:

image.png

但是如果传入的是一个空的数组,那如果不加任何的判断,将会返回一个 undefined 类型:

image.png

这就和题目要求的 never 不同,所以接下来我们只要实现这一步判断。

1.通过 extends 判断

在 ts 中的 extends 可以被用来作为一种约束,我们可以用它来约束我们的数组:

type First<T extends any[]> = T extends [] ? never : T[0];

2.通过 extends 和 'length' 判断

在上面的 js 当中,我们常见的可以通过数组的长度是否为 0 来判断一个数组是否是空数组,当然在 ts 中也能够这样添加判断,我们只需要去读取数组中的 'length' 属性:

type First<T extends any[]> = T["length"] extends 0 ? never : T[0];

3.通过 extends 和 [number] 判断

在之前我们有提到过,通过 T[number] 的方式,我们能遍历传入的数组,但是如果传入的数组为空的话,那么遍历的值会是什么呢

image.png

这场情况下为 union 类型

image.png

是不是发现了,在空数组的情况下,返回的是一个 never 类型,而不是上面的 undefined 类型,这是因为我们上面指定了位置为 0 的元素,而在下面的方式中并没有,而是返回了整个数组的类型,通过这个,我们就能够结合 extends 写出第三种题解

type First<T extends any[]> = T[0] extends T[number] ? T[0] : never

正常情况下的 T[0] 肯定是会存在于 T[number] 中的,但是当数组为空的时候 T[0] 为 undefined ,就不包含在 T[number] 的 never 中了。

4. inter(推断) 的使用

这里可以使用一个新的知识点,我们可以使用 inter 来做一个解题, inter 的使用有点类似于 es6 的解构赋值。

const [first, ...rest] = arr;

在 es6 中我们可以像上面一样去获取一个数组中的第一个元素也就是 first,剩下的元素就为 rest。

那么在 ts 中也一样

type First<T extends any[]> = T extends [infer First, ...infer Rest]
  ? First
  : never;

First 就是输入数组的第一个元素,其余元素都为 Rest,我们只需要拿到 First 的值就可以了。

知识点

关于上述提到了部分的知识点:

  1. inter(推断) 今天主要的新知识点就是 inter(推断) ,关于这个知识点我暂时没有找到相关的文档,只有一些文章教了如何去使用,具体的使用文档可以去官方找找看有没有。

TypeScript: Documentation - TypeScript 3.9 (typescriptlang.org)

总结

今天我们用了4种方法来解决第四道题,并且利用到了很多以后才会涉及到的知识点,这里可以多加理解一下4种不同的解题方式。