Не взаимодействуют типы в return и внутри хука (React + TypeScript)
Пытаюсь протипизировать return данного хука. Предполагаю, что всего могут быть 3 return'a, когда есть дата - остального нет, когда крутится лоадер – остального нет, когда выбрасывает ошибку – остального нет, но TS так не считает, вот получаю вот такую ошибку:
Type '{ data: ResponseDataType | null; isLoading: boolean; error: string | null; }' is not assignable to type '{ data: ResponseDataType; isLoading: false; error: null; } | { data: null; isLoading: true; error: null; } | { data: null; isLoading: false; error: string; }'. Type '{ data: ResponseDataType | null; isLoading: boolean; error: string | null; }' is not assignable to type '{ data: null; isLoading: false; error: string; }'. Types of property 'data' are incompatible. Type 'ResponseDataType | null' is not assignable to type 'null'. Type 'ResponseDataType' is not assignable to type 'null'. Type 'Record<string, unknown>' is not assignable to type 'null'.ts(2322)
Подскажите, пожалуйста, что я делаю не так?
import axios from "axios";
import { useEffect, useState } from "react";
export const useFetch = <ResponseDataType extends Record<string, unknown>>(
url: string
):
| {
data: ResponseDataType;
isLoading: false;
error: null;
}
| {
data: null;
isLoading: true;
error: null;
}
| {
data: null;
isLoading: false;
error: string;
} => {
const [data, setData] = useState<ResponseDataType | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
setIsLoading(true);
setError(null);
axios
.get<ResponseDataType>(url)
.then((response) => setData(response.data))
.then(() => console.log(data))
.catch((err) => {
if (axios.isAxiosError(err)) {
setError(err.message);
}
})
.finally(() => setIsLoading(false));
}, [url]);
return {
data,
isLoading,
error,
};
};
Ответы (2 шт):
Вы же описываете тип, а не все возможные значения. Попробуйте так:
export const useFetch = <ResponseDataType extends Record<string, unknown>>(
url: string
): {
data: ResponseDataType;
isLoading: boolean;
error: string;
} => {
const [data, setData] = useState<ResponseDataType>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string>(null);
useEffect(() => {
setIsLoading(true);
setError(null);
axios
.get<ResponseDataType>(url)
.then((response) => setData(response.data))
.then(() => console.log(data))
.catch((err) => {
if (axios.isAxiosError(err)) {
setError(err.message);
}
})
.finally(() => setIsLoading(false));
}, [url]);
return {
data: data,
isLoading: isLoading,
error: error,
};
};
Решил вот так:
import { useEffect, useState } from "react";
export function useFetch<ResponseDataType extends Record<string, unknown>>(
url: string
):
| {
data: null;
isLoading: false;
error: string;
}
| {
data: null;
isLoading: true;
error: null;
}
| {
data: ResponseDataType;
isLoading: false;
error: null;
} {
const [data, setData] = useState<ResponseDataType | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
setIsLoading(true);
setError(null);
const callApi = async () => {
try {
const response = await axios.get<ResponseDataType>(url);
setData(response.data);
} catch (err) {
if (axios.isAxiosError(err)) {
setError(err.message);
}
} finally {
setIsLoading(false);
}
};
callApi();
}, [url]);
if (isLoading) {
return {
data: null,
isLoading,
error: null,
};
}
if (error) {
return {
data: null,
isLoading: false,
error,
};
}
return {
data: data!,
isLoading: false,
error: null,
};
}