如何从action creator对象生成actions联合类型?
从嵌套Action Creator对象生成Action联合类型的TypeScript实现
需要实现类型工具MakeActions,从嵌套的Action Creator对象中提取所有Action的联合类型,期望效果如下:
type SomeActionCreator = { hoge: () => {type: 'hoge'} foo: () => {type: 'foo'} baz: { baz1: () => {type: 'baz1'} baz2: () => {type: 'baz2'} } } type SomAction = MakeActions<SomeActionCreator> // 期望结果:{type: 'hoge'} | {type: 'foo'} | {type: 'baz1'} | {type: 'baz2'}
原实现及报错
原代码
type ActionCreator = (...args: never[]) => {type: string} type ActionCreatorObj = { [name: string]: ActionCreator | ActionCreatorObj } type MakeActions<T extends ActionCreatorObj> = { [K in keyof T]: T[K] extends ActionCreator ? ReturnType<T[K]> : MakeActions<T[K]> }[keyof T] type SomeActionCreator = { hoge: () => {type: 'hoge'} foo: () => {type: 'foo'} baz: { baz1: () => {type: 'baz1'} baz2: () => {type: 'baz2'} } } type SomAction = MakeActions<SomeActionCreator>
报错信息
Type 'T[K]' does not satisfy the constraint 'ActionCreatorObj'. Type 'T[keyof T]' is not assignable to type 'ActionCreatorObj'. Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'ActionCreatorObj'. Type 'T[string]' is not assignable to type 'ActionCreatorObj'. Type 'ActionCreator | ActionCreatorObj' is not assignable to type 'ActionCreatorObj'. Type 'ActionCreator' is not assignable to type 'ActionCreatorObj'. Index signature for type 'string' is missing in type 'ActionCreator'.(2344)
问题原因
报错核心是递归调用MakeActions<T[K]>时,TypeScript无法确定T[K]一定符合ActionCreatorObj约束——T[K]可能是ActionCreator函数类型,而函数类型不满足ActionCreatorObj的索引签名要求。
解决方案
去掉强约束,动态判断当前值是Action Creator函数还是嵌套对象,调整后的代码如下:
// 定义Action Creator类型:返回包含type属性的对象的函数 type ActionCreator = (...args: any[]) => { type: string }; // 递归提取Action联合类型 type MakeActions<T> = { [K in keyof T]: // 如果是Action Creator函数,取其返回值 T[K] extends ActionCreator ? ReturnType<T[K]> // 如果是嵌套对象,递归处理 : T[K] extends object ? MakeActions<T[K]> // 非函数非对象的情况,返回never(目标场景不会触发) : never }[keyof T]; // 测试用例 type SomeActionCreator = { hoge: () => {type: 'hoge'} foo: () => {type: 'foo'} baz: { baz1: () => {type: 'baz1'} baz2: () => {type: 'baz2'} } } type SomAction = MakeActions<SomeActionCreator>; // SomAction 结果:{type: 'hoge'} | {type: 'foo'} | {type: 'baz1'} | {type: 'baz2'}
修改说明
- 移除
ActionCreatorObj定义,避免强约束带来的类型不兼容; - 在
MakeActions中先判断T[K]是否为ActionCreator类型,若是则取返回值; - 若不是函数则判断是否为对象类型,若是则递归处理嵌套结构;
- 通过
[keyof T]将所有属性的类型展开为联合类型。
内容的提问来源于stack exchange,提问作者green
相关产品推荐
相关产品推荐

