在TypeScript中写string.replace

图片

从4.1开始,TypeScript已经拥有了使用模板字面语法操作和转换字符串的能力。请看下面的例子:

 
type InternalRoute = `/${string}` const goToRoute = (route: InternalRoute) => {}

你可以使用任何以 / 开头的元素来调用 goToRoute 。但是任何其他字符串都是错误的。

你可以在模板文字类型中使用联合来扩展成更大的联合:

 
type EntityAttributes = `${'post' | 'user'}${'Id' | 'Name'}` // 'postId' | 'userId' | 'postName' | 'userName' 

甚至可以在模板文字中使用 infer 。

 
type GetLastName<TFullName extends string> =   TFullName extends `${infer TFirstName} ${infer TLastName}` ? TLastName : never

这里, ${infer TFirstName} ${infer TLastName} 表示任意两个字符串,中间有一个空格:

 
Matt PocockJimi HendrixCharles BarkleyEmmylou Harris

它实例化 TFirstName 和 TLastName 作为类型变量,如果它匹配传入的字符串,则可以使用它们。 ? TLastName 返回姓氏,这意味着你可以像这样使用 GetLastName :

 
type Pocock = GetLastName<'Matt Pocock'>// "Pocock"type Hendrix = GetLastName<'Jimi Hendrix'>// "Hendrix"type Barkley = GetLastName<'Charles Barkley'>// "Barkley" 

那么更高级的用例呢?如果我们想用破折号替换名称中的空格呢?

 
type ReplaceSpaceWithDash<TFullName extends string> =   TFullName extends `${infer TFirstName} ${infer TLastName}`    ? `${TFirstName}-${TLastName}`    : never type Name = ReplaceSpaceWithDash<'Emmylou Harris'>// "Emmylou-Harris"

很好,我们把结果改成 ${TFirstName}-${TLastName} 。现在,我们的类型变量似乎有点命名不当。让我们切换一下:

  • TFullName to TString

  • TFirstName to TPrefix

  • TLastName to TSuffix

 

type ReplaceSpaceWithDash<TString extends string> =   TString extends `${infer TPrefix} ${infer TSuffix}`     ? `${TPrefix}-${TSuffix}`     : never

现在它更通用了。但是还不够泛型——让我们让这个类型助手能够处理用另一个字符串替换任何字符串。

 
type Replace<  TString extends string,  TToReplace extends string,  TReplacement extends string,> = TString extends `${infer TPrefix}${TToReplace}${infer TSuffix}`   ? `${TPrefix}${TReplacement}${TSuffix}`   : never

我们把 TToReplace 和 - 和 TReplacement 交换了。这最终工作得很好:

 
type DashName = Replace<'Matt Pocock', ' ', '-'>// "Matt-Pocock"

除了,有几个bug。例如, never 看起来有点可疑。如果 Replace 没有找到任何 TToReplace ,它返回 never :

 
type Result = Replace<'Matt', ' ', '-'>// never

什么是正确的行为?我们想要返回传入的字符串:

 
type Replace<  TString extends string,  TToReplace extends string,  TReplacement extends string,> = TString extends `${infer TPrefix}${TToReplace}${infer TSuffix}`   ? `${TPrefix}${TReplacement}${TSuffix}`   : TString   type Result = Replace<'Matt', ' ', '-'>// "Matt"

第二个缺陷是它只替换一次。如果 TToReplace 有多个实例,则忽略第二个。

 
type DashCaseName = Replace<'Matt Pocock III', ' ', '-'>// "Matt-Pocock III"

这似乎是一个难以修复的bug——直到我们考虑 ${infer TPrefix}${TToReplace}${infer TSuffix} 是如何工作的。在像 Matt Pocock III 这样的字符串中,它会这样推断:

  • TPrefix : “Matt”

  • TSuffix: "Pocock III"


这意味着其余的工作需要在 TSuffix 上执行。再一次,这感觉很棘手——直到我们意识到可以递归地调用类型。这意味着我们可以将 TSuffix 括在 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 传递的内容:

首先, StringReplace<"Matt Pocock III", " ", "-"> 。返回 Pocock III 。

第二,  StringReplace<"Pocock III", " ", "-"> 。返回 III 。

最后, StringReplace<"III", " ", "-"> 。因为它找不到 " " 的任何实例,所以它只返回 TString (在本例中, "III" )。我们找到了递归循环的终点!

欢迎关注公众号:文本魔术,了解更多

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值