You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
什么意思呢?对于T extends U ? X : Y,当T是联合类型时,例如A | B | C,T extends U ? X : Y会被自动展开为:(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y),利用这个特性,配合infer关键字可以实现联合类型转交叉类型:
(T extends any ? (k: T) => void : never)会被展开为(A extends any ? (k: A) => void : never) | (B extends any ? (k: B) => void : never) | (C extends any ? (k: C) => void : never),即(k: A) => void | (k: B) => void | (k: C) => void,显然(k: A) => void | (k: B) => void | (k: C) => void会被(k: A & B & C) => void类型约束,那么使用infer就可以解出A & B & C了。
本文是在掘金的这篇文章(TS 4.1 新特性实现 Vuex 无限层级命名空间的 dispatch 类型推断。)的基础上进一步的实现,在阅读本文之前,可以先到掘金看看这篇文章。
TypeScript 4.1 类型模板字符串
先来看看TypeScript 4.1 beta 版本
首先在项目中安装 TypeScript 4.1 Beta 版本:
可以在任何地方使用类型模板字符串:
1、直接使用
上面的例子等价于:
2、在类型映射中使用
上面的例子等价于:
3、用于联合类型
上面的例子等价于:
4、常见的例子:用于事件定义
假设有一个函数
makeWatchedObject
,用于遍历一个对象,生成一个格式相同的对象,但是每次改变此对象属性时会跑出对应的事件,使用一个新的on
方法来检测属性的更改:对于需要监听的事件名,可以用类型模板字符串实现:
5、字符串转换工具函数
上面的例子等价于:
除
Uppercase
外,还有Lowercase
、Capitalize
、Uncapitalize
等工具函数6、类型映射中使用 as 子句映射类型模板字符串生成的新key
等价于:
问题描述
看下面这个例子,这是最常见的一个 Vuex 初始化方法:
我们希望实现
store.dispatch
时,可以出现智能提示和类型提示,如下图所示:那么问题描述变为通过
vuexOptions
,拿到一个action
名称到action
的payload
类型和dispatch
返回值的映射,等价于拿到如下类型:通过上面的
Actions
和Mutations
,我们可以很轻松地通过infer
拿到action
名称、payload
类型与dispatch
返回值类型,即推断actionFunction
的函数签名。推断 actionFunction 的函数签名
我们知道在Vuex初始化时,
action
一般是如下定义的:async homeAction(actionsContext: ActionContext<{}, {}>, homeContext: string) {}
,它的类型会被推断为:rootAction(actionsContext: ActionContext<{}, {}>, payload: string): Promise<void>
因此我们这里需要过滤掉第一个参数
ActionContext
,通过infer
就可以实现:获取root模块的Actions和Mutations映射
1、首先通过
infer
关键字拿到单个模块的所有actions
的类型2、再通过
in
关键字对actions
的keys做类型映射3、通过我们上面定义好的
GetRestFuncType
拿到actionFunction
的函数签名获取任一模块的Actions和Mutations映射
但是,上述的实现并不完善,对于非root模块,例如
detail
模块,例如想要dispatch
detail模块的detailAction
时,就要用到模板字符串拼接为detail/detailAction
:利用联合类型的自动展开,即可将
GetActionsTypes
通用化到非root模块:可以测试下如下代码:
拿到所有模块的Actions和Mutations映射
1、对于 root 模块而言,所有带命名空间的子模块都是放到
modules
属性下面,同样可以通过infer
关键字拿到modules
下所有模块的类型2、首先定义
GetModulesActionTypes
,通过in
关键字对keyof Modules
做类型映射,这样将拿到所有定义到modules
的Actions
和Mutations
映射3、然后定义
GetSubModuleActionsTypes
,通过infer
拿到SubModules
类型,并通过GetModulesActionTypes
拿到Actions
和Mutations
映射测试以下代码:
此时,
AllActionsUnion
的类型为:显然我们希望得到的是这样的类型:
这里我们需要将联合类型转化为交叉类型,我们可以利用Conditional Types in TypeScript
什么意思呢?对于
T extends U ? X : Y
,当T
是联合类型时,例如A | B | C
,T extends U ? X : Y
会被自动展开为:(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
,利用这个特性,配合infer
关键字可以实现联合类型转交叉类型:上面的
UnionToIntersection
可以理解为:当
T
为A | B | C
时:(T extends any ? (k: T) => void : never)
会被展开为(A extends any ? (k: A) => void : never) | (B extends any ? (k: B) => void : never) | (C extends any ? (k: C) => void : never)
,即(k: A) => void | (k: B) => void | (k: C) => void
,显然(k: A) => void | (k: B) => void | (k: C) => void
会被(k: A & B & C) => void
类型约束,那么使用infer
就可以解出A & B & C
了。通过 actionFunction 的函数签名拿到 payload 和 返回值
1、拿到
payload
:定义一个GetParam
泛型,通过infer
可推断出函数的第一个参数:2、拿到返回值:通过 TypeScript 内置的
ReturnType
可实现这里可以直接通过TypeScript的工具函数和
ReturnType
拿到,其原理也不复杂,就是通过infer
关键字:完整的实现
通过上面的方法,就可以实现
Vuex
的store.commit
和store.dispatch
类型判断,完整代码如下:小结
上面是笔者在Vuex中实践TypeScript模板字符串的总结,笔者也是顺手发布了一个npm 包,感兴趣的小伙伴看看vuex-typescript-commit-dispatch-prompt
使用方法
1、首先安装依赖,需要安装TypeScript 4.1 以上版本
2、在初始化store处引入
vuex-typescript-commit-dispatch-prompt
,然后拓展vuex module 的类型定义文章最后,笔者近期维护了一个公众号,用于分享前端新技术,欢迎大家到Counter的前端小站逛逛,一起关注前端的新技术发展。
The text was updated successfully, but these errors were encountered: