typescript 类型体操 之 110-medium-capitalize

520 阅读3分钟

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

前言

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

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

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

110-medium-capitalize

image.png

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

type cases = [
  Expect<Equal<MyCapitalize<'foobar'>, 'Foobar'>>,
  Expect<Equal<MyCapitalize<'FOOBAR'>, 'FOOBAR'>>,
  Expect<Equal<MyCapitalize<'foo bar'>, 'Foo bar'>>,
  Expect<Equal<MyCapitalize<''>, ''>>,
  Expect<Equal<MyCapitalize<'a'>, 'A'>>,
  Expect<Equal<MyCapitalize<'b'>, 'B'>>,
  Expect<Equal<MyCapitalize<'c'>, 'C'>>,
  Expect<Equal<MyCapitalize<'d'>, 'D'>>,
  Expect<Equal<MyCapitalize<'e'>, 'E'>>,
  Expect<Equal<MyCapitalize<'f'>, 'F'>>,
  Expect<Equal<MyCapitalize<'g'>, 'G'>>,
  Expect<Equal<MyCapitalize<'h'>, 'H'>>,
  Expect<Equal<MyCapitalize<'i'>, 'I'>>,
  Expect<Equal<MyCapitalize<'j'>, 'J'>>,
  Expect<Equal<MyCapitalize<'k'>, 'K'>>,
  Expect<Equal<MyCapitalize<'l'>, 'L'>>,
  Expect<Equal<MyCapitalize<'m'>, 'M'>>,
  Expect<Equal<MyCapitalize<'n'>, 'N'>>,
  Expect<Equal<MyCapitalize<'o'>, 'O'>>,
  Expect<Equal<MyCapitalize<'p'>, 'P'>>,
  Expect<Equal<MyCapitalize<'q'>, 'Q'>>,
  Expect<Equal<MyCapitalize<'r'>, 'R'>>,
  Expect<Equal<MyCapitalize<'s'>, 'S'>>,
  Expect<Equal<MyCapitalize<'t'>, 'T'>>,
  Expect<Equal<MyCapitalize<'u'>, 'U'>>,
  Expect<Equal<MyCapitalize<'v'>, 'V'>>,
  Expect<Equal<MyCapitalize<'w'>, 'W'>>,
  Expect<Equal<MyCapitalize<'x'>, 'X'>>,
  Expect<Equal<MyCapitalize<'y'>, 'Y'>>,
  Expect<Equal<MyCapitalize<'z'>, 'Z'>>,
]

从README和测试用例中能够得出,我们需要实现一个工具函数 MyCapitalize 它的作用和 TS 内置的 Capitalize 是完全相同的,能够将字符串字面量的第一个字符变成大写。

通过 JS 对比学习

我们需要实现一个函数,将传入的字符串的首位字母变成大写,通过JS内置的工具应该就能够很容易实现。

function myCapitalize(str:string){
  return str[0].toUpperCase() + str.slice(1,str.length)
}

image.png

但是有没有考虑过,如果不借助工具,我们需要怎么来实现呢,其实也很简单,因为字母小写转大写的方法也就是列出小写字母的左右情况,然后碰到了就用大写字母进行替换,所以我们只需要加一层映射就能够简单完成。这里 JS 就不写了,我们等一下的 TS 中也会使用这种思路。

实现 MyCapitalize

通过 JS 的实现思路,我们发现,题目十分的简单,只需要拿到字符串的第一个字母然后进行小写转大写就完成了,那么在 JS 中 存在着字符串方法 toUpperCase 来帮助字符串小写转大写,那么在 TS 有没有这种方法呢,答案当然是有的,我们通过 Uppercase 工具类型也能够实现字符串字面量的小写转大写:

type MYCA1 = Uppercase<'asd'>
// type MYCA1 = "ASD"

那么这道题就变得十分的简单,我们只需要单独拿出字符串字面量的第一位来进行转化,然后和剩下的字符串字面量拼接起来就完成了,关于结果的字符串字面量拼接,这个在前几天中也提到过,模板字面量也能够被用在结果当中。

type MYCA2 = 'asd' extends `${infer First}${infer Rest}`
  ?`${Uppercase<First>}${First}${First}`
  :never
// type MYCA2 = "Aaa"

所以我们只需要取到字符串的第一位进行大写转化之后,和剩下的拼接起来:

type MyCapitalize<S extends string> = S extends `${infer F}${infer Rest}` 
  ? `${Uppercase<F>}${Rest}`
  : S

要注意到测试用例中存在着会传入空字符的情况,那么我们在false分支中就需要包容这种情况,返回空字符串或者本身都是可以的。

然后就是上面说到的,我们如果不想使用 TS 提供的 Uppercase 工具类型,那么我们应该如何自己定义呢。

其实也很简单,我们只需要列出所有字母的情况,做一个映射表,将所有的小写字母映射为大写字母就可以了。

interface MyUppercase {
  a: "A"
  b: "B"
  c: "C"
  d: "D"
  e: "E"
  f: "F"
  g: "G"
  h: "H"
  i: "I"
  j: "J"
  k: "K"
  l: "L"
  m: "M"
  n: "N"
  o: "O"
  p: "P"
  q: "Q"
  r: "R"
  s: "S"
  t: "T"
  u: "U"
  v: "V"
  w: "W"
  x: "X"
  y: "Y"
  z: "Z"
}

type MYCA3 = MyUppercase["a"]
// type MYCA3 = "A"

当然这是最简单的实现,只能够进行单个字母的小写想大写转化,但是在这道题中已经足够了。

但是在我们使用这个接口的时候,会发现报错了:

image.png

这是因为,你没有办法保证入参F一定是这个接口中存在的属性,所以我们需要为这个F添加约束:

type MyCapitalize<S extends string> = S extends `${infer F extends keyof MyUppercase}${infer Rest}` 
  ? `${MyUppercase[F]}${Rest}`
  : S

这样测试就能够全部通过了,以上就是两种不同的解法。

知识点

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

  1. 模板字面量
  2. infer 关键字
  3. 条件链
  4. 索引取值

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

总结

今天我们做完了中等的第十三题,题型和昨天也是大部分相似,都是需要做字符串字面量进行操作,我们可以通过 TS 提供的内置工具类型,但是要知道,如果让你自己去实现,需要怎么去写这些工具类型。