typescript 类型体操 之 531-medium-string-to-union

123 阅读2分钟

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

前言

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

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

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

531-medium-string-to-union

image.png

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

type cases = [
  Expect<Equal<StringToUnion<''>, never>>,
  Expect<Equal<StringToUnion<'t'>, 't'>>,
  Expect<Equal<StringToUnion<'hello'>, 'h' | 'e' | 'l' | 'l' | 'o'>>,
  Expect<Equal<StringToUnion<'coronavirus'>, 'c' | 'o' | 'r' | 'o' | 'n' | 'a' | 'v' | 'i' | 'r' | 'u' | 's'>>,
]

从README和测试用例中能够得出,我们需要实现一个工具函数 StringToUnion 能够将字符串转化为联合类型。

利用 JS 进行模拟学习

由于JS中不存在联合类型的概念,我们用数组来进行代替,将一个字符串进行单个分割,并且放入一个数组中返回,值得注意的是,当字符串为空的时候,需要返回 never

function stringToUnion(str:string){
    let arr: any[] = []
    while(str) {
        arr.push(str[0])
        str = str.slice(1,str.length)
    }
    if(arr.length==0) {
        return 'never'
    }else {
        return arr
    }
}

image.png

实现 StringToUnion

那么 TS 根据 JS 的逻辑进行处理,首先我们需要一个参数用来保存最后需要的联合类型,所以需要在入参中定义一个默认的参数,类型为string,用来保存每个字符组成的联合类型:

type StringToUnion<T extends string,U extends string=''> = any

接着就是需要对字符串进行处理,判断字符串是否能够进行分割,可以的话就使用模板字面量获取首个字符和剩余字符,并且将剩下字符作为第一个参数,U和首个字符的联合类型作为第二个参数,递归回 StringToUnion。

type StringToUnion<T extends string,U extends string=''> = T extends `${infer S}${infer R}` 
    ? StringToUnion<R,U | `${S}`> 
    : U

然后就会发现,一开始的 U 是一个空字符串,所以这样写的话,会把这个空字符串也一并并入最后的联合类型。

image.png

所以我们需要对 U 进行初始值判断,刚开始的第一次递归,U 不应该被联合进第二个参数:

type StringToUnion<T extends string,U extends string=''> = T extends `${infer S}${infer R}` 
    ? U extends '' 
        ? StringToUnion<R,`${S}`> 
        : StringToUnion<R,U | `${S}`> 
    : U 

image.png

然后就是剩余一种特殊情况还未考虑,就是当输入的字符串是空字符串的时候,需要返回 never,所以我们对最后的答案进行判断,要是为空字符串的话,那就说明一开始输入的是空字符串,返回 never,不然就返回 U。

type StringToUnion<T extends string,U extends string=''> = T extends `${infer S}${infer R}` 
    ? U extends '' 
        ? StringToUnion<R,`${S}`> 
        : StringToUnion<R,U | `${S}`> 
    : U extends '' 
        ? never 
        : U

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

知识点

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

  1. 模板字面量
  2. 条件链

今天的题目没有新的知识点,主要是需要灵活运用入参定义参数和条件链之间的配合,来合理的构建我们需要的答案。

总结

今天我们做完了中等的第二十二题,题目属于比较常规的问题,和最近常见的题型非常的相似,主要都是通过定义参数以及条件链来进行判断以及特殊处理。