TypeScript:依赖内部属性的泛型配置数组类型推断问题
解决NgRx Effects通用工厂函数的类型推断问题
问题根源
- 初始版本用单个泛型
AC extends ActionCreator,要求数组中所有Config的action必须是同一类型,导致多类型action传入时报错。 - 修改为
AC extends ActionCreator[]后,Config<AC[number]>会将所有ActionCreator合并为联合类型,映射函数的参数变成联合类型的返回值,TypeScript无法为每个数组元素单独推断精确类型,只能得到宽泛的Object类型。
解决方案:使用元组泛型+映射类型
通过让泛型推导传入的数组为元组,并对元组的每个元素单独应用Config类型,确保每个mapping函数能精确对应其action的返回类型。
修改后的代码实现
// NgRx导入 import { ActionCreator } from '@ngrx/store'; import { createActionGroup, props } from '@ngrx/store'; // 测试用ActionGroup export const featureActions = createActionGroup({ source: 'MyGroup', events: { 'Update Entity': props<{ data: { updateName: string } }>(), 'Delete Entity': props<{ data: { deleteName: string } }>(), }, }); // Config类型定义(保持不变) export type Config<AC extends ActionCreator> = { action: AC; mapping: (data: ReturnType<AC>) => string; }; // 优化后的工厂函数 export function createMyEffects<AC extends ActionCreator[]>( effects: { [K in keyof AC]: Config<AC[K]> } ) { console.log(effects); } // 调用示例:类型推断正常,无报错 createMyEffects([ { action: featureActions.updateEntity, mapping: (updateData) => updateData.data.updateName, // updateData类型精确为ReturnType<typeof featureActions.updateEntity> }, { action: featureActions.deleteEntity, mapping: (deleteData) => deleteData.data.deleteName, // deleteData类型精确为ReturnType<typeof featureActions.deleteEntity> }, ]);
原理说明
- 泛型
AC extends ActionCreator[]会提示TypeScript将传入的数组字面量推断为元组类型(而非普通数组),元组中每个元素对应不同的ActionCreator类型。 - 映射类型
{ [K in keyof AC]: Config<AC[K]> }遍历元组的每个索引K,为每个位置生成对应的Config<AC[K]>类型,确保每个元素的mapping参数类型与自身action的返回类型严格绑定。
这样既解决了多类型action传入的类型冲突问题,又保留了每个mapping函数参数的精确类型推断。
内容的提问来源于stack exchange,提问作者Dmytro
相关产品推荐
相关产品推荐

