使用redux-actions重构TypeScript计数器示例遇错求助
问题描述
我是TypeScript新手,想要练习redux-actions库。先是跟着YouTube教程和redux-actions官方教程学习,但官方教程示例在jsfiddle或codepen中无法运行,于是决定修改YouTube教程的GitHub代码库,引入redux-actions库。
编写actions和reducer组件时遇到两个问题:
- increment和decrement出现错误提示「A computed property name must be of type 'string', 'number', 'symbol', or 'any'.ts(2464)」
- 不确定payload用any类型是否合适,有没有更好的类型定义方式
以下是我的代码尝试:
Counter.ts
export interface Counter { count: number; }
CounterActions.ts
import { createActions } from "redux-actions"; export const { increment, decrement } = createActions({ INCREMENT: (amount = 1) => ({ amount }), DECREMENT: (amount = 1) => ({ amount: -amount }), }); interface IncrementAction { type: typeof increment; } interface DecrementAction { type: typeof decrement; } export type CounterActionTypes = IncrementAction | DecrementAction;
CounterReducer.ts
import { handleActions } from "redux-actions"; import { CounterActionTypes, decrement, increment } from "./CounterAction"; import { Counter } from "./models/Counter"; const defaultState: Counter = { count: 0, }; export const counterReducer = handleActions( { [increment]: (state: Counter, { payload: { amount } }: any): Counter => { return { ...state, count: state.count + amount }; }, [decrement]: (state: Counter, { payload: { amount } }: any): Counter => { return { ...state, count: state.count + amount }; }, }, defaultState );
问题修复方案
1. 解决计算属性类型错误
increment和decrement是redux-actions生成的action creator函数,不能直接作为对象的计算属性名。需要通过toString()方法获取对应的action type字符串,或者直接使用定义的常量名。
方案一:使用action creator的toString()方法
修改后的CounterReducer.ts:
import { handleActions } from "redux-actions"; import { decrement, increment } from "./CounterAction"; import { Counter } from "./models/Counter"; const defaultState: Counter = { count: 0, }; export const counterReducer = handleActions( { [increment.toString()]: (state: Counter, { payload: { amount } }) => { return { ...state, count: state.count + amount }; }, [decrement.toString()]: (state: Counter, { payload: { amount } }) => { return { ...state, count: state.count + amount }; }, }, defaultState );
方案二:直接使用action type常量
先在CounterActions.ts中导出type常量:
import { createActions } from "redux-actions"; const actions = createActions({ INCREMENT: (amount = 1) => ({ amount }), DECREMENT: (amount = 1) => ({ amount: -amount }), }); export const { increment, decrement } = actions; // 导出action type常量 export const { INCREMENT, DECREMENT } = actions.types;
然后在CounterReducer.ts中使用:
import { handleActions } from "redux-actions"; import { DECREMENT, INCREMENT } from "./CounterAction"; import { Counter } from "./models/Counter"; const defaultState: Counter = { count: 0, }; export const counterReducer = handleActions( { [INCREMENT]: (state: Counter, { payload: { amount } }) => { return { ...state, count: state.count + amount }; }, [DECREMENT]: (state: Counter, { payload: { amount } }) => { return { ...state, count: state.count + amount }; }, }, defaultState );
2. 优化payload的类型定义
避免使用any类型,有两种常用的类型定义方式:
方法一:手动定义payload类型并结合redux-actions的Action类型
修改CounterActions.ts:
import { createActions, Action } from "redux-actions"; // 定义payload类型 interface AmountPayload { amount: number; } export const { increment, decrement } = createActions({ INCREMENT: (amount = 1): AmountPayload => ({ amount }), DECREMENT: (amount = 1): AmountPayload => ({ amount: -amount }), }); // 定义完整的action类型 export type IncrementAction = Action<AmountPayload>; export type DecrementAction = Action<AmountPayload>; export type CounterActionTypes = IncrementAction | DecrementAction;
然后在Reducer中指定action类型:
[increment.toString()]: (state: Counter, action: IncrementAction): Counter => { return { ...state, count: state.count + action.payload.amount }; }, [decrement.toString()]: (state: Counter, action: DecrementAction): Counter => { return { ...state, count: state.count + action.payload.amount }; },
方法二:通过ReturnType推导payload类型
在CounterActions.ts中添加类型推导:
import { createActions } from "redux-actions"; export const { increment, decrement } = createActions({ INCREMENT: (amount = 1) => ({ amount }), DECREMENT: (amount = 1) => ({ amount: -amount }), }); // 推导payload类型 export type IncrementPayload = ReturnType<typeof increment>['payload']; export type DecrementPayload = ReturnType<typeof decrement>['payload'];
在Reducer中使用推导的类型:
[increment.toString()]: (state: Counter, action: { payload: IncrementPayload }) => { return { ...state, count: state.count + action.payload.amount }; }, [decrement.toString()]: (state: Counter, action: { payload: DecrementPayload }) => { return { ...state, count: state.count + action.payload.amount }; },
免费学习资源推荐
- redux-actions官方文档:虽然示例在在线编辑器无法运行,但文档的API说明和用法逻辑很清晰,结合本地项目测试更高效
- TypeScript官方手册的Redux相关章节:讲解了TypeScript结合Redux的类型最佳实践
- YouTube实战教程:搜索"TypeScript redux-actions tutorial",选择近期更新、播放量较高的视频,跟着本地敲代码测试
- 开源项目参考:在GitHub搜索"redux-actions typescript",查看成熟项目中action和reducer的写法
内容的提问来源于stack exchange,提问作者Altaf
相关产品推荐
相关产品推荐

