TypeScript下Redux选择器抽象层级及类型推导可行性问询
Redux + TypeScript 选择器复用与类型推导实现方案
问题场景
原本使用Redux connect结合TypeScript的标准实现能正常推导props类型,但抽离选择器复用后,自定义的mapStateToProps包装函数无法让TypeScript正确推导mapState返回的props类型,导致ConnectedProps只能识别actions的类型。
原标准实现
import { connect, ConnectedProps } from 'react-redux' interface RootState { isOn: boolean } const mapState = (state: RootState) => ({ isOn: state.isOn, }) const mapDispatch = { toggleOn: () => ({ type: 'TOGGLE_IS_ON' }), } const connector = connect(mapState, mapDispatch) // 能正确推导类型:{isOn: boolean, toggleOn: () => void} type PropsFromRedux = ConnectedProps<typeof connector>
抽离选择器后的尝试
// selectors.js const selector1 = (state) => state.attr1; const selector2 = (state) => state.attr2; const selector3 = (state) => state.attr3;
import {selector1,selector3} from '../../selectors'; const selectors = { attr1:selector1, attr3:selector3, } // 自定义的mapState生成函数,但类型丢失 const mapStateToProps = (selectors: Selectors) => { return Object.keys(selectors).reduce((acc, selectorKey) => { acc[selectorKey] = selectors[selectorKey](state); return acc; }, {}); }; // 生成的connector无法推导mapState的props类型 const connector = connect(mapStateToProps(selectors), actions) type PropsFromRedux = ConnectedProps<typeof connector> // 仅能获取actions类型
可行方案与类型推导实现
这个复用选择器的方案完全可行,核心是通过泛型约束让TypeScript捕获选择器与最终props的类型关联,具体实现步骤如下:
1. 给选择器添加TypeScript类型
首先将selectors.js改为selectors.ts,并关联RootState类型:
// selectors.ts import { RootState } from './path-to-your-root-state'; // 导入全局RootState类型 export const selector1 = (state: RootState) => state.attr1; export const selector2 = (state: RootState) => state.attr2; export const selector3 = (state: RootState) => state.attr3;
2. 编写带泛型的mapState生成函数
通过泛型捕获选择器对象的键与返回值类型,确保生成的mapState函数有明确的类型标注:
import { RootState } from './path-to-your-root-state'; // 定义选择器映射的泛型类型:键对应最终props的键,值是接收RootState返回对应值的选择器 type SelectorMap<Props> = { [Key in keyof Props]: (state: RootState) => Props[Key]; }; // 生成mapState的函数,泛型P代表最终要映射到组件的props类型 const createMapState = <P>(selectors: SelectorMap<P>) => { return (state: RootState): P => { return Object.keys(selectors).reduce((acc, key) => { acc[key as keyof P] = selectors[key as keyof P](state); return acc; }, {} as P); }; };
3. 生成带类型的mapState并连接组件
使用createMapState生成mapState,此时TypeScript能完整推导mapState的返回类型,ConnectedProps也能获取完整的props类型:
import { connect, ConnectedProps } from 'react-redux'; import { selector1, selector3 } from './selectors'; import { toggleOn } from './your-actions-file'; // 确保actions有类型定义 // 定义选择器映射对象 const selectors = { attr1: selector1, attr3: selector3, }; // 生成带完整类型的mapState const mapState = createMapState(selectors); // mapDispatch可以是对象或函数(此处用对象示例) const mapDispatch = { toggleOn, }; // 创建连接器 const connector = connect(mapState, mapDispatch); // 此时能正确推导完整类型:{attr1: typeof selector1返回值, attr3: typeof selector3返回值, toggleOn: typeof toggleOn} type PropsFromRedux = ConnectedProps<typeof connector>;
关键原理说明
原来的实现中,mapStateToProps直接调用后返回的是无类型标注的普通对象,TypeScript无法识别其与选择器的关联。而带泛型的createMapState通过泛型P捕获了选择器对象对应的props结构,返回的mapState函数明确标注了返回类型P,让connect能正确识别mapState的输出类型,最终ConnectedProps可以合并mapState和mapDispatch的完整类型。
内容的提问来源于stack exchange,提问作者JmRag
相关产品推荐
相关产品推荐

