如何为React自定义useApi Hook指定返回数据类型解决TS报错
解决方案
核心是给useApi增加TypeScript泛型支持,让调用时可以动态传入返回数据和请求参数的类型,具体改造步骤如下:
1. 改造useApi.tsx
给Hook添加两个泛型参数:TData 定义接口返回的数据类型,TParams 定义请求入参的类型,同时补全useEffect依赖项避免逻辑bug:
import { AxiosResponse, AxiosPromise } from 'axios'; import { useState, useEffect } from 'react'; // 定义泛型:TData为返回数据类型,TParams为请求参数类型 const useApi = <TData = unknown, TParams = Record<string, unknown>>( apiFunction: (params: TParams) => Promise<AxiosResponse<TData>>, params: TParams = {} as TParams, callback?: (data: TData) => void ): [boolean, TData | null, null | string] => { const [data, setData] = useState<TData | null>(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState<null | string>(null); useEffect(() => { apiFunction(params) .then(({ data }: AxiosResponse<TData>) => { setData(data); setIsLoading(false); callback?.(data); }) .catch(() => { setError('Something went wrong'); setIsLoading(false); }); // 补全依赖项,参数变化时自动重新请求 }, [apiFunction, params, callback]); return [isLoading, data, error]; }; export default useApi;
2. 改造getMovie请求函数
修正返回类型,支持泛型传入返回数据类型:
import axios, { AxiosResponse } from "axios"; type getMovieParams = { _id: string }; const BASE_URL = "https://jsonplaceholder.typicode.com/todos/"; // 添加泛型T指定返回数据类型 const getMovie = <T,>(params: getMovieParams): Promise<AxiosResponse<T>> => { if (params) { const { _id } = params; if (_id) { const url = `${BASE_URL}/${_id}`; return axios.get<T, AxiosResponse<T>>(url); } } throw new Error("Must provide a query"); }; export default getMovie;
3. 组件内调用时指定泛型
调用useApi时传入你定义的Movie接口作为返回数据类型,同时调整渲染逻辑避免类型冲突:
import "./styles.css"; import useApi from "./useApi"; import getMovie from "./api"; interface Movie { userId: number; id: number; title: string; completed: boolean; } export default function App() { const idMovie = { _id: "3" }; // 回调函数参数类型直接指定为Movie const someAction = (data: Movie) => { // 执行业务逻辑 return data; }; // 调用时传入泛型:第一个是返回数据类型,第二个是请求参数类型 const [isLoading, data, error] = useApi<Movie, {_id: string}>(getMovie, idMovie, someAction); if (isLoading) { return <div>Loading...</div>; } if (error) { return <div>{error}</div>; } // 走到这里时error不存在,data一定是Movie类型,不会有类型报错 return ( <div> -- 获取电影数据1 -- <p>{data.title}</p> </div> ); }
内容的提问来源于stack exchange,提问作者yavgz
相关产品推荐
相关产品推荐

