typescript 类型体操 之 119-medium-replaceall

440 阅读3分钟

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

前言

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

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

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

119-medium-replaceall

image.png

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

type cases = [
  Expect<Equal<ReplaceAll<'foobar', 'bar', 'foo'>, 'foofoo'>>,
  Expect<Equal<ReplaceAll<'foobar', 'bag', 'foo'>, 'foobar'>>,
  Expect<Equal<ReplaceAll<'foobarbar', 'bar', 'foo'>, 'foofoofoo'>>,
  Expect<Equal<ReplaceAll<'t y p e s', ' ', ''>, 'types'>>,
  Expect<Equal<ReplaceAll<'foobarbar', '', 'foo'>, 'foobarbar'>>,
  Expect<Equal<ReplaceAll<'barfoo', 'bar', 'foo'>, 'foofoo'>>,
  Expect<Equal<ReplaceAll<'foobarfoobar', 'ob', 'b'>, 'fobarfobar'>>,
  Expect<Equal<ReplaceAll<'foboorfoboar', 'bo', 'b'>, 'foborfobar'>>,
  Expect<Equal<ReplaceAll<'', '', ''>, ''>>,
]

从README和测试用例中能够得出,我们需要实现一个工具函数 ReplaceAll 能够用来替换掉字符串中的全部字符串。

通过 JS 对比学习

在 JS 中,本身存在字符串方法 replace 如果是正常使用的话,只能够替换掉匹配的第一个字符串,但是可以使用

str.replace(/oldStr/g"newStr")

这种方式来进行全部替换。

image.png

实现 Replace

和昨天的题目类型,我们需要判断一个字符串中是否含有另个一个字符串,我最开始对这道题的不同想法就是,这道题不过是在昨天的基础上加上了递归,就是当我们匹配到第一个字符串之后,还要再去寻找下一个匹配的字符串,一直到不存在匹配的字符串了,说明替换就完成了

type ReplaceAll<
  S extends string,
  From extends string,
  To extends string
> = From extends ""
  ? S
  : S extends `${infer Before}${From}${infer After}`
  ? ReplaceAll<`${Before}${To}${After}`, From, To>
  : S;

type RAll1 = ReplaceAll<"foobarfoobar", "ob", "b">;
// type RAll1 = "fbarfbar"

但是这样写的递归存在着某些问题,就是匹配过一次的字符串还会被再次匹配到,但是测试中的用例代表着,一个字符串匹配过后和原有的进行拼接之后再出现的指定字符串是不算数的,所以上面这种递归方式是行不通的

那么要如何正确使用递归呢,上面的思路是如果当前字符存在第二个参数,那么就把第二个参数替换成第三个参数后的字符串作为第一个参数进入下一个递归。但是按照上面的说法,已经参与过递归的字符串,是不能够作为下一次判断的依据的,那么我们就可以,当字符串匹配成功之后,我们不是把整个的新字符串作为参数递归,而是只需要把After参数进行递归,因为After是还没有被匹配过的参数。

type ReplaceAll<
  S extends string,
  From extends string,
  To extends string
> = From extends ""
  ? S
  : S extends `${infer Start}${From}${infer After}`
  ? `${Start}${To}${ReplaceAll<After, From, To>}`
  : S;

type RAll1 = ReplaceAll<"foobarfoobar", "ob", "b">;
// type RAll1 = "fobarfobar"

这样到这里,测试用例就能够完全通过了。

知识点

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

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

今天的知识点都是之前有提到过了,在这里就不在做过多的描写,不懂得可以去看一下之前的文章或者相关题型。

总结

今天我们做完了中等的第十五题,题型和之前几天的都非常相似,并且同一道题的递归方式有很多种,我们就需要从中挑选我们需要的方式。