I have a custom react hook to do a API calls:
const useFetch: (string) => Record<string, any> | null = (path: string) => {
const [data, setData] = useState<Record<string, any> | null>(null);
var requestOptions: RequestInit = {
method: "GET",
redirect: "follow",
};
const fetcher = async () => {
const res = await fetch(
process.env.REACT_APP_API_ENDPOINT + path,
requestOptions
);
const json = await res.json();
setData(json.data);
};
useEffect(() => {
fetcher();
}, [path]);
return data;
};
My problem is that I use this hook to call many different endpoints, so I cannot specify the type for data in the hook. But when I call the hook, I know what the data is for that specific endpoint, so I want to specify a type for the variable storing the return value like:
const data: { name: string } | null = useFetch('/example-end-point');
This is what I want to achieve, but I get this error: Property 'name' is missing in type 'Record<string, any>' but required in type '{ name: string; }'.
Using any for the return type works, but I don't want to resort to that if there are any other ways to achieve this.
This is something that Generic Types can solve, you can absolutely allow for variable types to be passed into your single hook with a little bit of a code re-write.
Take a look...
const useFetch: <T>(string) => Record<T> | null = (path: string) => {
const [data, setData] = useState<Record<T> | null>(null);
var requestOptions: RequestInit = {
method: "GET",
redirect: "follow",
};
const fetcher = async () => {
const res = await fetch(
process.env.REACT_APP_API_ENDPOINT + path,
requestOptions
);
const json = await res.json();
setData(json.data);
};
useEffect(() => {
fetcher();
}, [path]);
return data;
};
and then when you call it you can do the following.
type MyData = {
name: string,
}
const data: MyData | null = useFetch<MyData>('/example-end-point');
You'd probably want to use unknown instead of any then narrow down the type elsewhere by performing some type guarding ie
const useFetch: (string) => unknown = (path: string) => {
...
const user = useFetch('/example-end-point')
if (!user || !user.name) throw new Error("Bad data")
const data: { name: string } = user
This way you get runtime data checking as well as type checking.