TypeScript 4.1 新特性:字符串模板类型,Vuex 终于有救了?

简介: TypeScript 4.1 快要发布了,老爷子 Anders Hejlsberg[1] 加入了一项重大更新,「字符串模板类型」 的支持。昨天看到这个更新的我特别兴奋,曾几何时,只要一遇到字符串拼接相关的类型,TypeScript 就束手无策了

TypeScript 4.1 快要发布了,老爷子 Anders Hejlsberg[1] 加入了一项重大更新,「字符串模板类型」 的支持。昨天看到这个更新的我特别兴奋,曾几何时,只要一遇到字符串拼接相关的类型,TypeScript 就束手无策了,比如:

  • Vuex 中加了 namespace 以后,dispatch 一个 mutation type 会带上前缀 dispatch('cart/add')
  • lodashget 方法,可以对一个对象进行 get(obj, 'a.b.c') 这样的读取。

现在 4.1 加入的这个新功能让这一切都拥有了可能。


基础语法


它的语法和 es 里的字符串模板很相似,所以上手成本也很低,先看几个例子:

type EventName<T extends string> = `${T}Changed`;
type T0 = EventName<'foo'>;  // 'fooChanged'
type T1 = EventName<'foo' | 'bar' | 'baz'>;  // 'fooChanged' | 'barChanged' | 'bazChanged'
type Concat<S1 extends string, S2 extends string> = `${S1}${S2}`;
type T2 = Concat<'Hello', 'World'>;  // 'HelloWorld'

字符串模板中的联合类型会被展开后排列组合:

type T3 = `${'top' | 'bottom'}-${'left' | 'right'}`;
// 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'


新增关键字


为了这个功能,老爷子在 TS 中新增了 uppercase, lowercase, capitalize, uncapitalize 这些关键字,用于对模板粒度字符串变量进行处理。

type Cases<T extends string> = `${uppercase T} ${lowercase T} ${capitalize T} ${uncapitalize T}`;
type T11 = Cases<'bar'>;  // 'BAR bar Bar bar'

其实很简单,就是提供了几个处理方法:大写、小写,首字母大写,首字母小写。


配合 infer


特别强大的一点是,模板字符串可以通过 infer 关键字,实现类似于正则匹配提取的功能:

type MatchPair<S extends string> = S extends `[${infer A},${infer B}]` ? [A, B] : unknown;
type T20 = MatchPair<'[1,2]'>;  // ['1', '2']
type T21 = MatchPair<'[foo,bar]'>;  // ['foo', 'bar']

通过 , 分割左右两边,再在左右两边分别用一个 infer 泛型接受推断值 [${infer A},${infer B}],就可以轻松的重新组合 , 两边的字符串。

配合 ... 拓展运算符和 infer递归,甚至可以实现 Join 功能:

type Join<T extends (string | number | boolean | bigint)[], D extends string> =
    T extends [] ? '' :
    T extends [unknown] ? `${T[0]}` :
    T extends [unknown, ...infer U] ? `${T[0]}${D}${Join<U, D>}` :
    string;
type T30 = Join<[1, 2, 3, 4], '.'>;  // '1.2.3.4'
type T31 = Join<['foo', 'bar', 'baz'], '-'>;  // 'foo-bar-baz'


实战运用


实现 Vuex namespace 推断:


type VuexOptions<M, N> = {
   namespace?: N,
   mutations: M,
}
type Action<M, N> = N extends string ? `${N}/${keyof M & string}` : keyof M
type Store<M, N> = {
   dispatch(action: Action<M, N>): void
}
declare function Vuex<M, N>(options: VuexOptions<M, N>): Store<M, N>
const store = Vuex({
   namespace: "cart" as const,
   mutations: {
      add() { },
      remove() { }
   }
})
store.dispatch("cart/add")
store.dispatch("cart/remove")

前往 Playground[2] 尝试一下~


实现 lodash get 函数:


type PropType<T, Path extends string> =
    string extends Path ? unknown :
    Path extends keyof T ? T[Path] :
    Path extends `${infer K}.${infer R}` ? K extends keyof T ? PropType<T[K], R> : unknown :
    unknown;
declare function get<T, P extends string>(obj: T, path: P): PropType<T, P>;
const obj = { a: { b: {c: 42, d: 'hello' }}};
const value = get(obj, "a.b.c")

前往 Playground[3] 尝试一下~


总结


TypeScript 4.1 带来的这个新功能让 TS 支持更多字符串相关的拼接场景,其实是特别实用的,希望各位看了以后都能有所收获~


参考资料


[1]Anders Hejlsberg: https://github.com/ahejlsberg

[2]Playground: https://www.typescriptlang.org/play?ts=4.1.0-pr-40336-8#code/C4TwDgpgBAagrhAHgeTMAlgewHYGcA8AsgDRQByAfFALxQDeAUFM9gIYC2EuYrAxhAH4AXOWJNm7OMFYYcuESQYBfBg1CQoAQV6zsRUpRrkoSYBGwATXFFzAATumwBzKAKgADACR0ySgPTeANYQIJgAZlCEUABkNvaOTkruUCLBoRGEqurQAMrAmHYQ+uRUtIzMUBbo3DK8ABYAFHy6Itq6xZQAlCIAbpjoFsqqFhC8ADashVBhcNg6WNiwCIgdFA2YaAvyS0iougQkJd1QeQVFh5SqvHLAcWdG8EgN5SwcXDz8IgBEvJPAX1BWNZrnhgGIKpJpPsRC8KoCLBYGp16FAlOC4VBCuxMD0IEiUSoKiolJ1VLYzgA6Ko1YD1Bo-P5+VgIr6k8mFKnVHi0xoMuzAPxYnEQVlAA

[3]Playground: https://www.typescriptlang.org/play?ts=4.1.0-pr-40336-8#code/C4TwDgpgBACgTgezAFXBAPMgNLAhsACyggA9gIA7AEwGcobg4BLCgcwD4oBeAKCn-qMWrYmUq08hKAH4oAVwoBrCggDuFKAC4+AmPiKly1OooggEAMyjIZ1gNp7CAXS07+jg2ONQABgBIAbxYLCDgoAGkAXwA6QODQqAAlSJ9bcNEjCVNzKxtZeCRUSEw7cKccRM5NeSUVdVcBGuU1CgBuHh4qCABjABtcOGgLBW7gJgQNVghgTBwYDPE6BmY2dgAKBAAjACtq7CgwfWqYAEpjxBQ0Wdh2dp5uiYYoLe3uKACoXGqPze-u6oALAAmHBUaoAcgIEF6vQQ4KgkURdweFCeADdcL05NAuFApsANjscAAiXDRTbRbrEk5AA

相关文章
|
2天前
|
监控 JavaScript 安全
TypeScript在员工上网行为监控中的类型安全实践
本文演示了如何使用TypeScript在员工上网行为监控系统中实现类型安全。通过定义`Website`类型和`MonitoringData`接口,确保数据准确性和可靠性。示例展示了从监控设备获取数据和提交到网站的函数,强调了类型定义在防止错误、提升代码可维护性方面的作用。
30 7
|
1天前
|
JavaScript 前端开发
TypeScript内置类型一览(Record<string,any>等等)(下)
TypeScript内置类型一览(Record<string,any>等等)
|
1天前
|
JavaScript
TypeScript内置类型一览(Record<string,any>等等)(中)
TypeScript内置类型一览(Record<string,any>等等)
|
1天前
|
JavaScript
TypeScript内置类型一览(Record<string,any>等等)(上)
TypeScript内置类型一览(Record<string,any>等等)
|
2天前
|
JavaScript 安全 前端开发
【TypeScript技术专栏】TypeScript中的类型推断与类型守卫
【4月更文挑战第30天】TypeScript的类型推断与类型守卫是提升代码安全的关键。类型推断自动识别变量类型,减少错误,包括基础、上下文、最佳通用和控制流类型推断。类型守卫则通过`typeof`、`instanceof`及自定义函数在运行时确认变量类型,确保类型安全。两者结合使用,优化开发体验,助力构建健壮应用。
|
2天前
|
JavaScript 前端开发 开发者
【TypeScript技术专栏】TypeScript类型系统与接口详解
【4月更文挑战第30天】TypeScript扩展JavaScript,引入静态类型检查以减少错误。其类型系统包括基本类型、数组等,而接口是定义对象结构的机制。接口描述对象外形,不涉及实现,可用于规定对象属性和方法。通过声明、实现接口,以及利用可选、只读属性,接口继承和合并,TypeScript增强了代码的健壮性和维护性。学习和掌握TypeScript的接口对于大型项目开发至关重要。
|
2天前
|
JavaScript 安全 前端开发
【亮剑】TypeScript 由于其强类型的特性,直接为对象动态添加属性可能会遇到一些问题
【4月更文挑战第30天】本文探讨了在 TypeScript 中安全地为对象动态添加属性的方法。基础方法是使用索引签名,允许接受任何属性名但牺牲了部分类型检查。进阶方法是接口扩展,通过声明合并动态添加属性,保持类型安全但可能导致代码重复。高级方法利用 OOP 模式的类继承,确保类型安全但增加代码复杂性。选择哪种方法取决于应用场景、代码复杂性和类型安全性需求。
|
2天前
|
JavaScript 前端开发
TypeScript基础类型
TypeScript基础类型
|
2天前
|
JavaScript 前端开发
typescript 混合类型
typescript 混合类型
|
2天前
|
JavaScript 前端开发 开发者
类型检查:结合TypeScript和Vue进行开发
【4月更文挑战第24天】TypeScript是JavaScript超集,提供类型注解等特性,提升代码质量和可维护性。Vue.js是一款高效前端框架,两者结合优化开发体验。本文指导如何配置和使用TypeScript与Vue:安装TypeScript和Vue CLI,创建Vue项目时选择TypeScript支持,配置`tsconfig.json`,编写`.tsx`组件,最后运行和构建项目。这种结合有助于错误检查和提升开发效率。