typescript 类型体操 之 106-medium-trimleft

269 阅读2分钟

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

前言

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

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

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

106-medium-trimleft

image.png

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

type cases = [
  Expect<Equal<TrimLeft<'str'>, 'str'>>,
  Expect<Equal<TrimLeft<' str'>, 'str'>>,
  Expect<Equal<TrimLeft<'     str'>, 'str'>>,
  Expect<Equal<TrimLeft<'     str     '>, 'str     '>>,
  Expect<Equal<TrimLeft<'   \n\t foo bar '>, 'foo bar '>>,
  Expect<Equal<TrimLeft<''>, ''>>,
  Expect<Equal<TrimLeft<' \n\t'>, ''>>,
]

从README和测试用例中能够得出,我们需要实现一个工具函数 TrimLeft 能够去除掉字符串前面的空格以及 \n\t

通过 JS 对比学习

我们需要实现一个函数,将传入的字符串前面的空格或者 \n\t 进行删除

function trimLeft(str: string){
  let s = ''
  for(let item of str){
    if(item != ' ' && item != '\n' && item != '\t'){
      s += item
    }
  }
  return s
}

image.png

实现 TrimLeft

通过 JS 的实现思路,我们需要去循环遍历输入的字符串,并且判断每一个字符是否为 空格或者 \n\t

但是在 TS 中,我们好像没有经历过什么方法,能够获取字符串的某一部分,但是这是有办法的,就是使用 TS 的模板字符串。

type TRIM1 = 'hhhh'

type TRIM2 = `${TRIM1}hhhh`
// type TRIM2 = "hhhhhhhh"

使用模板字面量我们就能够将不同的两个字符串字面量类型进行拼接,这点我们在之前的文章中有做过具体的介绍,今天就来介绍模板字面量的另一个用途--获取字符串的某一个部分。

我们只需要结合使用模板字面量和之前学习的 infer 关键字,还有 rest 参数,这一系类的知识,我们就能够获取字符串字面量类型中,特定的某一部分:

type TRIM3 = 'asdfg'

type TRIM4 = TRIM3 extends `${'a'}${infer rest}`?rest:never 
// type TRIM4 = "sd"

type TRIM5 = TRIM3 extends `${'a'}${infer rest1}${'f'}${infer rest2}`? `${rest1}${rest2}`:never 
// type TRIM5 = "sdg"

可以看到,借助模板字面量和 infer 关键字,我们能够自由的获取字符串中我们想要的那一部分字符串字面量,这样子,我们就能够用来判断上面所说的 空格或者 \n\t

首先我们可以用一个联合类型来保存下我们需要剔除的三种字符串,

type TrimLeftWord = ' '|'\n'|'\t'

然后就是利用上面描述的知识,就能够从字符串的前面选出这些部分。

type TrimLeftWord = ' '|'\n'|'\t'

type TrimLeft<S extends string> = S extends `${TrimLeftWord}${infer R}`?R:never

type TRIM6 = TrimLeft<'\n2'>
type TRIM6 = "\t2"

当然,这时候测试中就只会通过一个例子,因为我们现在只考虑到了一次的情况,字符串前面需要剔除的部分可能会有多个,所以需要在合适的位置上加入递归来完成操作。

type TrimLeftWord = ' '|'\n'|'\t'

type TrimLeft<S extends string> = S extends `${TrimLeftWord}${infer R}`?TrimLeft<R>:S

type TRIM6 = TrimLeft<'\n\t2'>
// type TRIM6 = "2"

这样测试就能够全部通过了

知识点

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

  1. 模板字面量
  2. infer 关键字
  3. 条件链

模板字面量在之前的文章中我们有做过简单的介绍,虽然没有提及今天的这种使用方法,但是也还是可以优先了解一下模板字面量的一个基础特点。

TypeScript 之 模板字面量 - 掘金 (juejin.cn)

总结

今天我们做完了中等的第十一题,题型整体是之前一直经常碰到的递归思路,虽然有一个没有接触过的模板字面量类型的使用方法,但是也算是在今天这道题中,学会了怎么去使用。