第 79 期 - TypeScript 字符串操作:模板字面量类型的应用
摘要
文章阐述了 TypeScript 4.1 版本后的模板字面量类型,通过联合类型、infer 关键字和递归,展示了在字符串操作中的应用,如定义类型、提取部分内容、替换字符等,并修正操作中的错误。
一、TypeScript 的模板字面量类型基础示例
在 TypeScript 4.1 版本后,可以使用模板字面量语法操作字符串。例如定义InternalRoute类型为/${string},这样函数goToRoute的参数route只能接收以/开头的字符串,否则会报错。这展示了模板字面量类型在简单的字符串类型定义上的应用。
二、模板字面量类型中的联合类型
- 可以在模板字面量类型中使用联合类型来扩展成更大的联合类型。例如
EntityAttributes类型定义为${'post' | 'user'}${'Id' | 'Name'},其结果是'postId' | 'userId' | 'postName' | 'userName'。这体现了联合类型在模板字面量类型中的组合效果。
三、模板字面量类型中的 infer 关键字
- 可以在模板字面量中使用
infer关键字。如GetLastName类型定义中,TFullName extends${infer TFirstName} ${infer TLastName}? TLastName : never,对于像Matt Pocock这样有空格分隔的字符串,infer会将其拆分为类型变量TFirstName和TLastName,然后通过? TLastName返回最后一个名字。例如type Pocock = GetLastName<'Matt Pocock'>,结果为"Pocock"。 - 对于更复杂的需求,如将名字中的空格替换为破折号。定义
ReplaceSpaceWithDash类型,TFullName extends${infer TFirstName} ${infer TLastName}?${TFirstName}-${TLastName}: never,例如type Name = ReplaceSpaceWithDash<'Emmylou Harris'>,结果为"Emmylou - Harris"。 - 为了让代码更通用,对类型变量重命名,将
TFullName改为TString,TFirstName改为TPrefix,TLastName改为TSuffix。
四、实现更通用的字符串替换类型
- 定义
Replace类型来实现更通用的字符串替换,type Replace<TString extends string, TToReplace extends string, TReplacement extends string>,当TString extends${infer TPrefix}${TToReplace}${infer TSuffix}时,返回${TPrefix}${TReplacement}${TSuffix},否则返回never。例如type DashName = Replace<'Matt Pocock', ' ', '-'>,结果为"Matt - Pocock"`。 - 但是这个类型存在两个问题,一是如果没有找到要替换的字符串会返回
never,这是不正确的,修改后的Replace类型在没有找到要替换的字符串时返回传入的字符串。例如type Result = Replace<'Matt', ' ', '-'>,结果为"Matt"。 - 第二个问题是只替换一次,如果有多个要替换的字符串实例,后面的将被忽略。例如
type DashCaseName = Replace<'Matt Pocock III', ' ', '-'>,结果为"Matt - Pocock III"。
五、通过递归解决多次替换问题
- 定义
StringReplace类型,通过递归调用解决多次替换的问题。type StringReplace<TString extends string, TToReplace extends string, TReplacement extends string>,当TString extends${infer TPrefix}${TToReplace}${infer TSuffix}时,返回${TPrefix}${TReplacement}${StringReplace<TSuffix, TToReplace, TReplacement>},否则返回TString。例如type Result = StringReplace<'Matt Pocock III', ' ', '-'>,结果为"Matt - Pocock - III"`。 - 在递归过程中,要注意避免无限循环。以
StringReplace<"Matt Pocock III", " ", "-">为例,第一次返回Pocock III,第二次返回III,最后因为找不到" ",直接返回TString(这里是"III"),从而结束递归循环。
