Home > Software design >  Why typescript is considering an object data as a boolean type?
Why typescript is considering an object data as a boolean type?

Time:01-23

I have a route in my server /feeds/category. When a GET request is made to that route I get the json as {message:"category list", data:{national:true, business:true, sports:false }}

Note: value of data is not static. It can vary from user to user. i.e data can be {national:false, business:true, sports:false } for one user and {national:false, business:false, sports:false } for another.

I have defined a custom hook to send a GET request to the server and fetch the data.

Here is the customer hook:

export const useGetApi = (link: string) => {
    const [data, setData] = useState();
    const [loading, setLoading] = useState(true);

    const handleFetch = async () => {
        const cancelToken = axios.CancelToken.source();
        try {
            const response = await axios.get(link, { cancelToken: cancelToken.token });
            setLoading(false);
            setData(response.data.data);
            //console.log(response.data);
        } catch (err) {
            setLoading(true);
            //error handling logic
        }
    };

    useEffect(() => {
        handleFetch();
    }, [JSON.stringify(data)]);

    return [data, loading];
};

Since I had to use this category list data at multiple places so I thought to provide the data using a context provider. Here is my context logic:

//typescript types

export type ChildrenProps = {
    children?: ReactNode | undefined;
};

export type CategoryListProps = {
    national: boolean;
    business: boolean;
    sports: boolean;
};

export type CategoryListContextType = {
    list: CategoryListProps;
};


const CategoryList = createContext<CategoryListContextType | null>(null);

export const useCategoryList = () => {
    const contextValue = useContext(CategoryList);
    if (!contextValue) {
        throw new Error("CategoryList context must be used inside CategoryList provider");
    }

    return contextValue;
};

export const CategoryListProvider: React.FC<ChildrenProps> = (props) => {
    const [data, loading] = useGetApi("/feeds/category");
    const value = { data };

    return <CategoryList.Provider value={value}>{props.children}</CategoryList.Provider>;
};

I could see a red line below value and it shows typescript error as

return <CategoryList.Provider value={value}>{props.children}</CategoryList.Provider>;
                              ^

Type '{ list: boolean | undefined; }' is not assignable to type 'CategoryListContextType'.

I am not sure why it is treating the value of data as a boolean

Please guide me on what I am doing wrong.

CodePudding user response:

The issue is how you defined your custom useGetApi hook's state:

const [data, setData] = useState();

Here the type of data would be undefined since you didn't specify the type for that state and didn't pass a default value either.

And your custom hook returns this:

return [data, loading];

So the type of the return would be (boolean | undefined)[] because loading is of type boolean and data is of type undefined, so when you destructure it, both data and loading would be type boolean | undefined.

Consider the following changes:

Return the array as readonly so you have proper type of data and loading separated when destructuring the array:

return [data, loading] as const;

Change useGetApi to accept a generic type and properly type the data state:

export const useGetApi = <T,>(link: string) => {
  const [data, setData] = useState<T>();
// ...

And then use it as:

  const [data, loading] = useGetApi<CategoryListProps>('/feeds/category');

This will still leave you with data being type CategoryListProps | undefined, which is correct since data could have never been set and was initialized with undefined, it is up to you to properly handle that case when you use it in inside CategoryListProvider.

  • Related