typescript 类型体操 之 15-medium-last

147 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

前言

在学习typescript的过程当中,有一个github库对其类型的学习特别有帮助,是一个有点类似于leetcode的刷题项目,能够在里面刷各种关于typescript类型的题目,在上一篇文章中,我们完成了中等的第六题,今天来做中等的第七题 15-medium-last

下面这个是类型体操github仓库:

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

15-medium-last

image.png

import type { Equal, Expect } from '@type-challenges/utils'

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

从README和测试用例中能够得出,题目需要我们实现一个工具类型 Last,用于获取数组的最后一个元素。

利用 JS 进行对比学习

1. 数组最后一个索引

首先最简单的方法,通过返回数组的最后一个索引来获得最后一个元素。

function last(arr) {
    return arr[arr.length-1]
}

image.png

递归判断

利用递归判断当前的数组的个数,一个的话直接返回,超过一个就去掉首位,将剩下的数组再次放入方法当中。

function last(arr) {
    if(arr.length == 1){
        return arr[0]
    }else{
        arr.shift()
        return last(arr)
    }
}

image.png

实现 Last

通过 JS 的实现,我们大概能够得到几个思路,一个是通过数组的长度来获取最后一个元素,另一个是通过递归来一个一个的剔除掉第一个元素,直到拿到最后一个元素。

通过数组的 'length' 属性实现

通过索引类型我们就能够拿到数组的某一个位置的值,并且TS中也是能够通过length这个属性来获取长度的:

type GetLength<T extends any[]> = T['length']

type B1 = GetLength<[() => 123, { a: string }]>
// type B1 = 2

但是存在一个问题,数组的长度并不是数组的最后一项的下标,但是再 TS 中我们没办法执行减一这样的操作。

type Last<T extends any[]> = T[T['length']]

type B1 = Last<[() => 123, { a: string }]>
// type B1 = undefined

但是我们能够稍微取巧一下,我给当前这个数组最前面加一项不就可以了吗,并且我们能够通过解构赋值,很方便的完成这一步。

type Last<T extends any[]> = [any , ...T][T['length']]

type B1 = Last<[() => 123, { a: string }]>
// type B1 = {
//     a: string;
// }

这里的第一项只是一个占位符,是什么类型都是无所谓的,他只是为了让我们把最后一项的下标变成数组长度,这样子,我们就能够拿到最后一项了。

通过递归实现

JS 的解法中还有另外一种,就是通过不断地移除掉第一个元素并且递归,实现到最后数组中只剩下一个元素来获取,这个元素就是末尾的元素。

在 TS 中我们能够通过 infer 关键字来获取数组中的第一个元素和后面的数组,只需要去判断一下当前数组中是不是只有一个元素,不是的话将后面的数组丢入递归,是的话就返回这个唯一的元素。

type Last<T extends any[]> = T extends [infer R]
    ? R 
    :  T extends [infer first,...infer rest] 
        ? Last<rest> 
        : never

type B1 = Last<[() => 123, { a: string }]>
// type B1 = {
//     a: string;
// }

infer + 解构

在上面的那一种实现方法中,我们是通过infer来获取数组的第一项,但是 infer 加 解构 不止是能获取第一项,还可以获取到数组的末尾项,其中的写法和取第一项是完全一样的。

type Last<T extends any[]> = T extends [...infer R, infer rest] 
    ? rest 
    : never;

这样子,我们也能够顺利的拿到了数组的最后一项

知识点

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

  1. 解构赋值
  2. 数组的 'length' 属性
  3. infer 关键字

本文共用了三种不同的解法来解答了这道题,题目涉及到的知识点大部分都是重复的,在这里就不再过多的重复了,不懂得小伙伴能够取阅读一下之前的相关文章。

总结

今天我们做完了中等的第七题,题目的解法很多,但是大部分知识点我们都是碰到过的,只是在于你是否能够灵活的运用,这点就是算法题的精髓所在,语法都在这里,但是会不会用,就是另一回事了。