使用扩展运算符结合嵌套对象与useReducer hook时遇TypeScript类型错误
嵌套对象状态在useReducer中的TypeScript类型错误问题
状态类型定义
interface name { firstName: string; lastName: string; } type NameType = name; interface employer { name: string; state: string; } type EmployerType = employer; interface person { name: NameType; age: number; employer: EmployerType; } type PersonType = person; const defaultPerson: PersonType = { name: { firstName: "The", lastName: "Rock" }, age: 25, employer: { name: "Noone", state: "Nowhere" } };
useState中更新嵌套对象正常工作
在useState里使用二级扩展运算符更新嵌套对象完全正常:
export default function App() { const [person, setPerson] = useState<PersonType>(defaultPerson); function handleInputChange(input: string) { setPerson({ ...person, name: { ...person.name, firstName: input } }); } return ( <div className="App"> <input onChange={(e) => handleInputChange(e.target.value)} /> <h2>{JSON.stringify(person, null, 4)}</h2> </div> ); }
useReducer中相同操作触发TypeScript错误
但在useReducer里用同样的扩展运算符更新时,TypeScript会抛出类型错误:
interface action { type: string; fieldName: string; value: string | number; } type ActionType = action; function reducer(state: PersonType, action: ActionType) { switch (action.type) { case "firstName": { return { ...state, name: { ...state.name, firstName: action.value } }; } } return state; } export default function App() { const [person, dispatch] = useReducer(reducer, defaultPerson); return ( <div className="App"> <input onChange={(e) => dispatch({ type: "test", fieldName: "firstName", value: e.target.value }) } /> <h2>{JSON.stringify(person, null, 4)}</h2> </div> ); }
临时解决方法(非最优)
目前可以通过分步复制状态的方式临时绕过错误,但这并非最优实现:
const newState = {...state} const newName = {...newState.name} newName.firstName = action.value newState.name = newName return newState
问题根源与正确解决思路
问题出在ActionType的类型定义:action.value被宽泛定义为string | number,在firstName分支中,TypeScript无法确认此时的value一定是符合firstName要求的string类型,因此抛出类型不匹配错误。
正确解决方案:使用可辨识联合类型定义Action
通过给Action定义可辨识联合类型,让TypeScript能根据action.type推断出对应value的具体类型:
// 为每个字段的更新动作定义专属类型,type作为辨识符关联value类型 type ActionType = | { type: "firstName"; value: string } | { type: "lastName"; value: string } | { type: "age"; value: number } | { type: "employerName"; value: string } | { type: "employerState"; value: string }; function reducer(state: PersonType, action: ActionType) { switch (action.type) { case "firstName": { return { ...state, name: { ...state.name, firstName: action.value // 此时TypeScript明确value是string,类型完全匹配 } }; } case "lastName": { return { ...state, name: { ...state.name, lastName: action.value } }; } case "age": { return { ...state, age: action.value }; } // 其他字段的更新分支同理 default: return state; } }
修改后,TypeScript能准确识别每个分支中action.value的类型,使用扩展运算符更新嵌套对象时就不会再报错,同时类型检查也能正常生效。
内容的提问来源于stack exchange,提问作者Ashish Verma
相关产品推荐
相关产品推荐

