I have implemented client
function that accepts an optional formatter
parameter which is either function
or undefined
, even though I'm checking if the formatter
is undefined
or not, and return the result based on that inside client
, but TS seems to ignore the check and show the return value wrong. What am I missing in here?
Function implementation:
import axios from "axios";
import { Required } from "utility-types";
import { AxiosRequestConfig } from "axios";
const client = async <D, F>(
{ url, ...config }: Required<AxiosRequestConfig, "url">,
formatter?: (data: D) => F
) => {
const response = await axios.request<D>({
url: encodeURI(url),
...config,
});
if (typeof formatter === "undefined") {
return response;
}
return formatter(response.data);
};
Usage:
const formatHomeData = (data: HomePageResponse) => {
return {
products: data.result.bestSell, // This will result in an Array
slides: data.result.sliders,
notification: data.result.alert,
};
};
export const fetchIndex = async () => {
const url = 'http://localhost:4000/home';
const formatted = await client({ url }, formatHomeData);
const products = formatted.products; // TS complains here
};
fetchIndex();
TS Error
Property 'products' does not exist on type '{ products: FormattedProduct[]; slides: SliderItemResponse[]; notification: string; } | AxiosResponse<HomePageResponse>'.
Property 'products' does not exist on type 'AxiosResponse<HomePageResponse>'.ts(2339)
By the way, I'm using Generics, so that I don't have to keep defining the return type of formatter
functions explicitly each time like this:
interface FormatHomeDataReturnType {
products: FormattedProduct[];
slides: SliderItemResponse[];
notification: string;
}
Here's a link to TypeScript Playground in the minimalist way I could make it.
CodePudding user response:
It would be really nice if Typescript could infer this automatically, but it's not so automatic. It does give you a way to specify it explicitly using overloads though.
Overloads are a way to define the return type for different variations of a function signature. For a generic async lambda like in your example, this looks like:
type ClientOverload = {
<D, F>(urlAndConfig: Required<AxiosRequestConfig, 'url'>): Promise<AxiosResponse<D>>;
<D, F>(urlAndConfig: Required<AxiosRequestConfig, 'url'>, formatter: (data: D) => F): Promise<F>;
};
const client: ClientOverload = async <D, F>(...) { ... };
If you add the overload explicitly stating how defining formatting
affects the return type, you won't get an error trying to use the products
property.