TypeScript自定义useUpdate Hook泛型类型声明报错咨询
问题说明
自定义Hook useUpdate 初始实现如下,泛型推导存在优先级问题:
import { useRef, useEffect } from 'react'; type CallBack<T extends readonly any[]> = ( ...args: T ) => void | (() => void | undefined); function useUpdate<T extends readonly any[]>( callback: CallBack<T>, deps: T ) { const didMount = useRef(false); const prevDeps = useRef(deps); useEffect(() => { if (!didMount.current) { didMount.current = true; return; } const clean = callback(...prevDeps.current); prevDeps.current = deps; return () => { if (clean) clean(); }; }, deps); }
使用时回调声明入参的场景校验正常,无入参回调会触发类型错误:
const state: boolean = useSomethingElse(); // 正常运行无类型报错 useUpdate((prevState) => { console.log('对比前后状态', prevState, state); },[state]); // 触发类型报错 useUpdate(() => { console.log('仅感知状态更新', state); },[state]); // 报错信息:Argument of type '[boolean]' is not assignable to parameter of type '[]'. // Source has 1 element(s) but target allows only 0.ts(2345)
问题核心原因:TypeScript默认会从所有匹配泛型的参数位置推导类型,回调无入参时会优先将泛型T推导为空元组[],和实际传入的deps类型冲突。
解决方案
通过NoInfer类型工具阻断回调参数位置的泛型推导,让TS仅从deps参数位置推导泛型T即可。
- TS 5.4及以上版本可直接使用内置
NoInfer工具类型 - 低版本TS可自行实现等效类型:
type NoInfer<T> = T extends infer U ? U : never
修改后的类型定义(内部逻辑无需调整):
// 低版本TS放开下面注释即可 // type NoInfer<T> = T extends infer U ? U : never; type CallBack<T extends readonly any[]> = ( ...args: NoInfer<T> // 阻断该位置的泛型推导优先级 ) => void | (() => void | undefined); function useUpdate<T extends readonly any[]>( callback: CallBack<T>, deps: T ) { const didMount = useRef(false); const prevDeps = useRef(deps); useEffect(() => { if (!didMount.current) { didMount.current = true; return; } const clean = callback(...prevDeps.current); prevDeps.current = deps; return () => { if (clean) clean(); }; }, deps); }
修改后类型表现完全符合预期:
- 泛型
T始终以传入的deps类型为准,不会被无参回调推导为空元组 - 回调入参的类型校验依然生效,如果声明的入参类型和
deps类型不匹配,会正常抛出类型错误 - 无参回调场景不再触发误报
内容的提问来源于stack exchange,提问作者HYD
相关产品推荐
相关产品推荐

