使用TypeScript与React-Redux时如何推断映射Props?
嘿,这个问题其实挺常见的——很多用TypeScript开发React-Redux应用的同学都吐槽过这种重复定义类型的样板代码。下面我给你分享几种可以避免重复的方案,以及聊聊现在的常规实践是啥样的:
1. 用TypeScript工具类型自动推导Props
TypeScript的ReturnType工具类型,或者React-Redux官方提供的ConnectedProps类型,都能帮你从mapStateToProps(和mapDispatchToProps)的返回值自动生成组件的Props类型,完全不用手动写重复的接口。
方案一:用ReturnType提取mapState的返回类型
如果只有mapStateToProps的话,直接用ReturnType<typeof mapStateToProps>就能拿到对应的Props类型:
import React from 'react'; import { connect } from 'react-redux'; // 先定义全局Redux State的类型 interface MyState { counter: number; } // 定义mapStateToProps,类型自动推断返回值 const mapStateToProps = (state: MyState) => { return { counter: state.counter }; }; // 自动推导Props类型,无需手动写AppProps接口 type AppProps = ReturnType<typeof mapStateToProps>; class App extends React.Component<AppProps> { render() { return <div>当前计数:{this.props.counter}</div>; } } export default connect(mapStateToProps)(App);
方案二:用ConnectedProps处理更复杂的情况
如果你的组件同时用到了mapDispatchToProps,React-Redux提供的ConnectedProps会更方便——它能自动合并mapState和mapDispatch的返回类型:
import React from 'react'; import { connect, ConnectedProps } from 'react-redux'; interface MyState { counter: number; } const mapStateToProps = (state: MyState) => ({ counter: state.counter }); const mapDispatchToProps = { increment: () => ({ type: 'INCREMENT' as const }), decrement: () => ({ type: 'DECREMENT' as const }) }; // 先创建连接器 const connector = connect(mapStateToProps, mapDispatchToProps); // 从连接器自动推导完整的Props类型 type AppProps = ConnectedProps<typeof connector>; class App extends React.Component<AppProps> { render() { const { counter, increment, decrement } = this.props; return ( <div> <p>当前计数:{counter}</p> <button onClick={increment}>+1</button> <button onClick={decrement}>-1</button> </div> ); } } export default connector(App);
2. 函数组件+useSelector:更简洁的类型安全方案
如果你已经切换到函数组件开发,React-Redux的useSelector Hook本身就能提供完美的类型推断,连connect和Props定义都可以省略:
import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; interface MyState { counter: number; } const App = () => { // 自动推断counter的类型为number const counter = useSelector((state: MyState) => state.counter); const dispatch = useDispatch(); return ( <div> <p>当前计数:{counter}</p> <button onClick={() => dispatch({ type: 'INCREMENT' as const })}>+1</button> </div> ); }; export default App;
这种方式现在已经成为React-Redux开发的主流推荐,不仅类型安全,代码也更简洁。
关于“常规写法”的疑问
早期TypeScript工具类型和React-Redux类型支持还不完善的时候,很多开发者确实会手动重复定义Props接口,这算是当时的“常规写法”。但随着TypeScript和React-Redux的迭代升级,用自动推导的方式来避免类型重复已经成为更标准、更高效的实践,完全不需要再写那种重复的样板代码了。
内容的提问来源于stack exchange,提问作者amoe

