如何在TypeScript的useReducer Hook中重载Payload类型?
问题:useReducer中Action类型定义引发TypeScript报错
我在函数组件中初始化了useReducer Hook,需要在listReducer中实现两个动作:
- CREATE:将state的
list赋值为User[]类型数组,payload应为User[] - DELETE:通过
_id(字符串类型)删除单个元素,payload应为字符串
当前定义的Action类型如下:
type Action = { type: ActionKind, payload: User[] | string; }
触发TypeScript报错:TS2339: Property 'list' does not exist on type 'string | User[]'
尝试将payload定义为包含list(User[])和id(string)的对象,但必填属性会引发错误;设为可选属性又会出现list: User[] | undefined和id: string | undefined的问题,请问如何正确定义Action类型解决该问题?
相关代码
type State = { list: User[]; } const initialListState: State = { list: [], }; enum ActionKind { CREATE = 'CREATE', DELETE = 'DELETE', } function listReducer(state: State, action: Action): State { const { type, payload } = action; switch (type) { case ActionKind.CREATE: return { list: action.payload }; case ActionKind.DELETE: return { list: state.list.filter((el) => el._id !== action.payload._id), }; default: return state; } } export const DashBoard = () => { const [state, dispatch] = useReducer(listReducer, initialListState); useEffect(() => { if (allUsers && allUsers.length > 0) { dispatch({ type: ActionKind.CREATE, payload: allUsers, }); } }, [allUsers]);
export interface User { _id: string; name: string; age: string; email: string; password: string; isAdmin: boolean; }
解决方案:用联合类型区分不同动作的Action
核心思路是为每个ActionKind定义专属的Action类型,再通过联合类型组合,让TypeScript能根据type自动推断对应payload的类型,实现类型窄化。
修改后的Action类型定义
// 为CREATE动作定义专属Action类型 type CreateAction = { type: ActionKind.CREATE; payload: User[]; }; // 为DELETE动作定义专属Action类型 type DeleteAction = { type: ActionKind.DELETE; payload: string; }; // 组合成联合类型 type Action = CreateAction | DeleteAction;
修改后的Reducer逻辑
function listReducer(state: State, action: Action): State { switch (action.type) { case ActionKind.CREATE: // TypeScript自动推断payload为User[]类型 return { list: action.payload }; case ActionKind.DELETE: // TypeScript自动推断payload为string类型 return { list: state.list.filter((el) => el._id !== action.payload), }; default: return state; } }
原理说明
联合类型让每个动作的type和payload形成一一对应的绑定关系,TypeScript在switch分支中会自动进行类型窄化:当匹配到ActionKind.CREATE时,会自动认定action是CreateAction类型,payload必然是User[];匹配到ActionKind.DELETE时同理,payload必然是字符串,彻底消除了联合类型的歧义问题。
内容的提问来源于stack exchange,提问作者Paul Gutsal
相关产品推荐
相关产品推荐

