基于React状态的TypeScript动态类型:useFetch钩子类型报错问题
解决useFetch钩子的TypeScript类型推导问题
核心问题在于你的联合类型成员没有做到严格互斥,导致TypeScript无法将钩子返回的对象精准匹配到FetchResult<TData>的某一个成员。要实现状态与属性的联动推导,必须确保每个状态对应的属性组合明确且互斥。
1. 重新定义严格互斥的联合类型
修正FetchResult的类型结构,让每个状态的属性完全对应:
// 加载中状态:loading为true,data和error均为undefined type FetchLoading = { status: 'loading'; loading: true; data: undefined; error: undefined; }; // 错误状态:loading为false,data为undefined,error存在 type FetchError = { status: 'error'; loading: false; data: undefined; error: Error; }; // 成功状态:loading为false,data存在,error为undefined type FetchSuccess<TData> = { status: 'success'; loading: false; data: TData; error: undefined; }; // 联合类型:只能是以上三种状态之一 type FetchResult<TData> = FetchLoading | FetchError | FetchSuccess<TData>;
2. 修正useFetch钩子的实现
确保钩子返回的状态严格匹配联合类型的某个成员,避免动态混合属性导致TypeScript无法推断:
import React from 'react'; function useFetch<TData>(url: string): FetchResult<TData> { // 初始状态严格匹配FetchLoading类型 const [state, setState] = React.useState<FetchResult<TData>>({ status: 'loading', loading: true, data: undefined, error: undefined, }); React.useEffect(() => { const fetchData = async () => { try { const response = await fetch(url); const data = await response.json() as TData; // 更新为明确的FetchSuccess类型 setState({ status: 'success', loading: false, data, error: undefined, }); } catch (err) { // 更新为明确的FetchError类型 setState({ status: 'error', loading: false, data: undefined, error: err as Error, }); } }; fetchData(); }, [url]); return state; }
3. 组件中的类型守卫使用
现在TypeScript可以通过状态值自动推导属性的存在性:
function DataComponent() { const result = useFetch<{ id: number; name: string }>('/api/user'); // loading为true时,自动推断为FetchLoading类型,无data/error if (result.loading) { return <div>加载中...</div>; } // status为'error'时,自动推断为FetchError类型,error必存在 if (result.status === 'error') { return <div>请求失败:{result.error.message}</div>; } // 剩余情况自动推断为FetchSuccess类型,data必存在 return <div>用户名:{result.data.name}</div>; }
错误原因分析
之前的报错本质是:
- 钩子返回的对象同时保留了所有状态的属性(比如success状态下仍存在error的可能值),导致TypeScript认为返回值是联合类型的交叉而非某个明确成员
status字面量与loading布尔值没有严格绑定(比如status: 'success'但loading可能为true),破坏了类型联动的前提
内容的提问来源于stack exchange,提问作者R.M. Reza
相关产品推荐
相关产品推荐

