TypeScript重载签名不兼容:useTabs钩子加入setCurrentTab报错
自定义useTabs Hook的类型兼容问题
初始问题场景
当从返回接口useTabsReturnType中移除setCurrentTab属性时,代码可正常运行;但添加该属性后,触发类型不兼容报错。
初始代码
interface useTabsReturnType<T> { currentTab: T; setCurrentTab: React.Dispatch<React.SetStateAction<T>>; } export function useTabs(defaultValues: number): useTabsReturnType<number>; // 报错:This overload signature is not compatible with its implementation signature.ts(2394) export function useTabs(defaultValues: string): useTabsReturnType<string>; export function useTabs(defaultValues?: string | number): useTabsReturnType<string | number> { const [currentTab, setCurrentTab] = useState(defaultValues || ''); return { currentTab, setCurrentTab, }; }
修改后出现的新错误
按照Radu Diță的建议调整后,返回语句中的setCurrentTab属性触发新的类型错误:
Type 'Dispatch<SetStateAction<string | number>>' is not assignable to type 'Dispatch<SetStateAction<string>> | Dispatch<SetStateAction<number>>'. Type 'Dispatch<SetStateAction<string | number>>' is not assignable to type 'Dispatch<SetStateAction<string>>'. Type 'SetStateAction<string>' is not assignable to type 'SetStateAction<string | number>'. Type '(prevState: string) => string' is not assignable to type 'SetStateAction<string | number>'. Type '(prevState: string) => string' is not assignable to type '(prevState: string | number) => string | number'. Types of parameters 'prevState' and 'prevState' are incompatible. Type 'string | number' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'.ts(2322) useTabs.ts(20, 3): The expected type comes from property 'setCurrentTab' which is declared here on type 'useTabsReturnType<number> | useTabsReturnType<string>'
问题原因与解决办法
核心原因
- 初始重载冲突:实现签名返回
useTabsReturnType<string | number>,但重载签名要求返回单一类型的useTabsReturnType,TypeScript无法自动收敛联合类型到单一类型,尤其是setCurrentTab的dispatch类型存在逆变特性,导致重载不兼容。 - Dispatch类型不兼容:
Dispatch<SetStateAction<string | number>>不能赋值给Dispatch<SetStateAction<string>> | Dispatch<SetStateAction<number>>,因为dispatch函数参数是逆变的——接受更宽泛类型的dispatch,无法被当作接受更窄类型的dispatch使用。
修复代码
用泛型重构,让TypeScript自动推导类型,避免手动重载的兼容问题:
import { useState, Dispatch, SetStateAction } from 'react'; interface useTabsReturnType<T> { currentTab: T; setCurrentTab: Dispatch<SetStateAction<T>>; } export function useTabs<T extends string | number>(defaultValue?: T): useTabsReturnType<T> { const [currentTab, setCurrentTab] = useState<T>(defaultValue ?? ('') as T); return { currentTab, setCurrentTab, }; }
说明
- 泛型
T约束输入为string | number,TypeScript会根据传入的默认值自动推导返回类型。 defaultValue ?? ('') as T处理默认值为空的场景,保证类型匹配。- 移除手动重载,通过泛型实现类型安全,同时解决dispatch的逆变类型冲突问题。
内容的提问来源于stack exchange,提问作者Nathan Tew
相关产品推荐
相关产品推荐

