我的博客原文:https://blog.gplane.win/posts/ts-template-string-types.html
介绍
在明天的早些时候,Anders Hejlsberg 在 TypeScript 的仓库中发了一个 Pull Request:Template string types and mapped type as
clauses。这个个性预计会在 4.1 版本中可用。
具体给 TypeScript 带来什么新个性,我就不在这里反复说了,Anders Hejlsberg 在 PR 中介绍得很分明。这篇文章次要探讨利用这个模板字符串类型,来对字符串字面量类型进行一些典型的字符串操作。
革除字符串的特定前缀
实现与剖析
JavaScript 的字符串中有一个实例办法 trimStart
,它能够去掉字符串后面的空格字符。咱们将在 TypeScript 的类型层面上实现这个性能。代码如下:
type Whitespace = '' |'\n'|'\r'|'\t'
type TrimStart<S extends string, P extends string = Whitespace> =
S extends `${P}${infer R}` ? TrimStart<R, P> : S
第 1 行的 Whitespace
类型定义了罕用的一些空格字符。当然 Unicode 中还定义了其它的一些空格字符,咱们依据须要往 Whitespace
这个联结类型前面补充就能够。
第 3 行至第 5 行就是 TrimStart
类型的定义。这个类型须要两个类型参数,其中 S
是要被解决的字符串,P
是要被搜寻并删除的前缀。这两个参数都指定 string
类型作为泛型束缚。另外 TypeScript 容许咱们为类型参数提供一个默认类型,在这里,类型参数 P
的默认类型是 Whitespace
,示意在不指定要搜寻哪些字符串的时候,默认搜寻空格字符。
咱们利用 conditional types 来让 TypeScript 剖析字符串 S
是否匹配咱们指定的 pattern:以 P
为结尾,前面追随任意字符串,同时利用 infer
关键字将前面的字符串提取进去用作被返回的类型。
如果传入的字符串匹配该 pattern,则会递归地 trim 上来,直到不再匹配这个带有指定前缀的字符串为止。
如果传入的字符串没带有该前缀或者曾经实现前缀删除,就会将 S
类型原样返回。
利用
type String1 = '\t \r \n value'
type Trimmed1 = TrimStart<String1>
type String2 = '---value'
type Trimmed2 = TrimStart<String2, '-'>
以上的例子中,Trimmed1
和 Trimmed2
的类型都是 'value'
。
当不给 TrimStart
类型传入第二个类型参数时,默认应用 Whitespace
类型;若提供,则能够删除指定的前缀。
字符串替换
实现与剖析
这里我别离实现了单次替换和全副替换。代码如下:
type ReplaceOnce<Search extends string, Replace extends string, Subject extends string> =
Subject extends `${infer L}${Search}${infer R}` ? `${L}${Replace}${R}` : Subject
type ReplaceAll<Search extends string, Replace extends string, Subject extends string> =
Subject extends `${infer L}${Search}${infer R}` ? ReplaceAll<Search, Replace, `${L}${Replace}${R}`> : Subject
ReplaceOnce
类型和 ReplaceAll
类型须要三个类型参数:Search
类型是要被搜寻的字符串;Replace
类型用于找到 Search
后,替换掉原来的 Search
;Subject
就是要被解决的字符串。
ReplaceOnce
类型和 ReplaceAll
类型很类似:都用到了 conditional types,而且条件还雷同。不同的是条件判断通过后返回的类型不同。
在下面的 conditional types 的条件中,咱们对 Subject
字符串进行 pattern 匹配:查看字符串中是否蕴含 Search
字符串。如果蕴含,则还利用 infer
关键字将位于 Search
右边、左边的字符串提取进去用于返回。须要留神的是,只管在咱们的 pattern 中 Search
在两头,但这并不意味着 Search
肯定要在两头才算匹配——放在结尾或开端也是能够的(放在开端时,可能存在某些 edge cases 不能被匹配,这应该是 TypeScript 的问题),而这时候 infer L
或 infer R
推断进去的类型是一个空字符串字面量类型,即 ''
类型。
如果字符串匹配咱们的 pattern,则利用传入的 Replace
字符串代替原来的 Search
字符串,同时应用之前提取进去的类型 L
和类型 R
来组成新的字符串:${L}${Replace}${R}
。
到这里,咱们曾经实现一次替换了。对于 ReplaceOnce
类型,当初就能够将刚刚生成的新字符串类型返回;而对于 ReplaceAll
类型,则将这个新的字符串类型作为 Subject
参数,再次传入 ReplaceAll
类型中而后递归上来,直到所有字符串都被替换。
利用
type String2 = 'process'
type Replaced1 = ReplaceOnce<'s', 'x', String2>
type Replaced2 = ReplaceOnce<'ss', 'x', String2>
type Replaced3 = ReplaceAll<'s', 'x', String2>
在下面的例子中,Replaced1
的类型是 'procexs'
,Replaced2
的类型是 'procex'
,Replaced3
的类型是 'procexx'
。后果合乎预期。
查看是否蕴含指定的子字符串
实现与剖析
这个能够由下面的「字符串替换」简化而来:
type IsIncludes<Needle extends string, Haystack extends string> =
Haystack extends `${infer L}${Needle}${infer R}` ? true : false
Needle
就是要查找的子字符串,Haystack
就是要被搜寻的字符串。conditional types 中的 pattern 与下面「字符串替换」中的 pattern 一样。最初依据匹配与否返回 true
类型或 false
类型即可。
利用
type String2 = 'process'
type Included = IsIncludes<'pro', String2>
type NotIncluded = IsIncludes<'pre', String2>
在下面的例子中,Included
的类型为 true
,NotIncluded
的类型为 false
。
最初,这里有我写的一份代码,我把它放在 TypeScript 的 Playground 上。
另外,自己最近在寻找前端岗位的工作。分割邮箱:g-plane#hotmail.com
。
全文完。