skip to Main Content

I want the useFetch hook to accept any kind of array to be used as the type of data:

hooks/useFetch.ts:

const useFetch = <T extends any[]>(dataUrl: string) => {
  const [data, setData] = useState<T>([]);
  const [error, setError] = useState<string | null>(null);
  const result = await axios(dataUrl);
  
  setData(result.data);
}

Note: result is of type AxiosResponse<any, any>, and the successful result.data should be an object array. This is the full code of the hook.

App.tsx:

interface City {
  rank: number;
  city: string;
  state: string;
  population: string;
}

const { data: cities, error } = useFetch<City[]>('/data.json');

I’m getting the following error on this line const [data, setData] = useState<T>([]);:

Argument of type ‘never[]’ is not assignable to parameter of type ‘T | (() => T)’.

How to remove this error?

2

Answers


  1. TypeScript doesn’t know what type the empty array is, and so it is assigned to never[] by default. Casting the array to T via useState<T>([] as T) will also give you issues due to the same issue you’re currently running into. Unless you explicitly need your generic to extend an array, you can simplify your useFetch declaration to:

    const useFetch = async <T>(dataUrl: string) => {
      const [data, setData] = useState<T[]>([]);
      const [error, setError] = useState<string | null>(null);
      const result = await axios(dataUrl);
    
      setData(result.data);
    };
    
    Login or Signup to reply.
  2. If you had tried casting the useState() parameter to T, you would have seen an error like

    Conversion of type ‘never[]’ to type ‘T’ may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to ‘unknown’ first.

    The super simple version is to tell Typescript you know what you’re doing and to shut up with the following…

    const useFetch = <T extends any[]>(dataUrl: string) => {
      const [data, setData] = useState<T>([] as unknown as T);
    

    Another approach you might want to try is creating an array-specific version of your hook, eg useFetchArray.

    const useFetchArray = <T,>(dataUrl: string) => {
      const [data, setData] = useState<T[]>([]); // 👈 note the T[] type here
      const [error, setError] = useState<string | null>(null);
    
      // ...
    }
    

    With the consumer using

    const { data: cities, error } = useFetchArray<City>('./data.json');
    

    This avoids any ambiguity around generic array types.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search