You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 22:55:17