Typescript 字符串模板的类型编程实践

字符串模板是 TypeScript 4.1 新增的特性,可以对字符串进行更多的限制,下面通过简单介绍它的来源,进而来解决实际开发当中的问题。

背景知识


在 TypeScript 中,字符串可以是类型比如:

type name = 'Lucy';

const username: name = 'LI Lei';
// Type '"LI Lei"' is not assignable to type '"Lucy"'.

TypeScript 4.1 中,带来了 Template Literal Types,它可以用来限制模板字符串,比如:

type World = "world";
 
type Greeting = `hello ${World}`;
// type Greeting = "hello world"

实际需求


现在有个实际需求,限制一个请求参数的类型,它的值可能是

"contains(extension_number,'变量')"
"contains(extension_number,'变量') or contains(display_name,'变量')"
"contains(extension_number,'变量') or contains(display_name,'变量') or contains(name,'变量')"

解决方案


观察三个字符串都有相同的结构单元contains(字段,'变量'),那么首先来实现这个单元

// 为了扩展性,泛型 T 为字段名称
type FilterSearch<T> = `contains(${T},'${string}')`;

观察这个字段可以进行枚举,直接使用字符串类型来实现

// 用所需字段来限制泛型T
type Field= `extension_number` | `display_name` | 'name';
type FilterSearch<T extends Field> = `contains(${T},'${string}')`;

此时需求中第一个字符串可以通过以上类型进行限制了

type Field= `extension_number` | `display_name` | 'name';
type FilterSearch<T extends Field> = `contains(${T},'${string}')`;

const extSearch: FilterSearch<'extension_number'> = `contains(extension_number,'${string}')`;

然后就是实现or语句,使其变为可选的

...
type FuzzySearch<F extends Field, S extends Field, T extends Field> =
    F extends Field ?
      S extends Field ?
        T extends Field ?
          `${FilterSearch<F>} or ${FilterSearch<S>} or ${FilterSearch<T>}`
        : `${FilterSearch<F>} or ${FilterSearch<S>}`
      : FilterSearch<F>
    : never

以上类型实现了以下作用:

const key: string = `搜索值`;
const params: FuzzySearch<'extension_number', `display_name`, 'name'> = 
`contains(extension_number,'${key}') or contains(display_name,'${key}') or contains(name,'${key}')`;

再眼看需求,观察到除了extension_number以外,其他的字段都是可选的,为了方便使用,增加泛型的默认值:

...
type FuzzySearch<F extends Field = 'extension_number', S extends Field | '' = '', T extends Field | '' = ''> =
    F extends Field ?
    S extends Field ?
      T extends Field ?
          `${FilterSearch<F>} or ${FilterSearch<S>} or ${FilterSearch<T>}`
        : `${FilterSearch<F>} or ${FilterSearch<S>}`
      : FilterSearch<F>
    : never

在使用时可以根据需要来传入查询的字段:

const params: FuzzySearch<'extension_number', `display_name`, 'name'> = 
`contains(extension_number,'${key}') or contains(display_name,'${key}') or contains(name,'${key}')`;

const params1: FuzzySearch = `contains(extension_number,'${key}')`;

const params2: FuzzySearch<'extension_number', `name`> = 
`contains(extension_number,'${key}') or contains(name,'${key}')')`;

需注意的问题


由于我们在字符串模板中定义了变量,所以一下这种形式的代码也可以通过类型检查:

const params1: FuzzySearch<'extension_number'> = `contains(extension_number,'101') or contains(name,'101')`;
                                                                             ⬆ 此段字符会被认为字符串变量 ⬆

但是以下的类型检查依然是无法通过的:

const params2: FuzzySearch<'extension_number','display_name'> = 
`contains(extension_number,'${key}') or contains(name,'${key}')')`;
// Type '`contains(extension_number,'${string}') or contains(name,'${string}')')`' is not assignable to type '`contains(extension_number,'${string}') or contains(display_name,'${string}')`'.

也就是说对于该类型,只要传了第二或者第三泛型参数,类型限制将会完全如意料中的生效;
如果只传了第一个泛型参数或者不传,将没办法完全限制类型:因为字符串内可以是任意字符

总结


通过泛型、extends、默认参数限制了一个字符串模板的形状。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容