React:在 setState() 中使用“额外属性检查”以避免细微错误
发布: (2026年3月9日 GMT+8 12:27)
3 分钟阅读
原文: Dev.to
Source: Dev.to
问题
type State = {
name: string,
count: number,
}
// Initial state
const [state, setState] = useState({ name: 'Unknown', count: 0 });
// Intended update
function resetCount() {
setState(state => ({
...state,
count: 0,
}));
}
// Oops – typo goes unnoticed
setState(state => ({
...state,
cont: 0, // typo of `count`
}));
因为展开运算符会生成一个完整的 State 对象,拼写错误的 cont 被视为多余属性,类型检查器不会报错。这在重命名时(例如把 count 改为 personCount)可能悄悄出现,并导致细微的 bug。
解决方案:为更新函数标注返回类型
显式声明传给 setState 的函数的返回类型,即可强制执行多余属性检查。
type State = {
name: string,
personCount: number, // renamed from `count`
};
const [state, setState] = useState({ name: 'Unknown', personCount: 0 });
function resetCount() {
setState((state): State => ({
...state,
count: 0, // ❌ TypeScript error: 'count' does not exist in type 'State'
}));
}
现在拼写错误(或已过时的属性名)会在构建时被捕获。
替代方案
对复杂状态使用 useReducer
显式为 reducer 的返回值标注类型同样会触发多余属性检查。
// ❌ Inferred return type – no excess‑property checking
function reducer(state: State, action: Action) {
/* ... */
}
// ✅ Explicit return type – catches incorrect properties
function reducer(state: State, action: Action): State {
/* ... */
}
使用 Immer 进行可变式更新
Immer 提供了一个代理,使你能够编写“变异”代码,同时保持不可变性。TypeScript 仍会校验属性名。
注意: 这不是 React 的惯用模式,可能会降低某些团队的可读性。
使用 Partial 编写辅助更新函数
// Helper that merges a partial state update
function update(partial: Partial) {
setState(s => ({
...s,
...partial,
}));
}
function resetCount() {
update({ cont: 0 }); // ❌ Error: 'cont' does not exist in type 'State'
}
进一步阅读
- 类型兼容性文档 – 解释了多余属性检查背后的设计决策。