字符串模板是 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
、默认参数限制了一个字符串模板的形状。