Как фиксированный тип ‘unknown’ не может быть присвоен типу ‘Country[] в React typescript

#typescript #react-hooks #react-tsx

#typescript #реагирующие крючки #react-tsx

Вопрос:

Я получил ошибку TS2322: тип ‘unknown’ нельзя присвоить типу ‘Country[]’

Ошибка

страницы / Страны /index.tsx

Полный код: https://gitlab.com/fResult/countries-workshop/-/blob/bug/src/pages/Countries/index.tsx

 import * as ApiCountries from '../../services/APIs/countries.api'

function Countries() {
  const findCountriesCallback = useCallback(() => ApiCountries.findAll(), [])
  const { execute: fetchCountries, value: countries } = useAsync(findCountriesCallback)

  useEffect(() => {
    ;(async () => await fetchCountries())()
  }, [fetchCountries])

  return (
    <InfiniteScroll
      items={countries}     // I don't understand why `countries` is `unknown` type
      renderEmptyList={() => (
        <div className="text-light-text dark:text-dark-text">
          No Content
        </div>
      )}
      keyExtractor={({ alpha3Code }) => alpha3Code}
      renderItem={renderItem}
      className="flex flex-col mt-8 md:flex-row md:flex-wrap gap-14 justify-between"
    />
  )
}
 

ххххххххххххххххххххххххххххххххххххх

Это три файла, которые я считаю соответствующими.

services / API/countries.api.ts

(API из https://restcountries.eu /)

 const BASE_URL = 'https://restcountries.eu/rest/v2'
export async function findAll() {
  return await fetch(`${BASE_URL}/all`)
}

export async function findByName(name: string) {
  return await fetch(`${BASE_URL}/name/${name}`)
}

 

ххххххххххххххххххххххххххххххххххххх

useAsync.tsx

(эта реализация перехвата для обработки успеха и ошибки асинхронной функции)
Полный код: https://gitlab.com/fResult/countries-workshop/-/blob/bug/src/cores/hooks/useAsync.tsx

 function useAsync<T, E>(
  asyncFn: (params?: any) => Promise<Response>,
  immediate: boolean = true
) {
  const [value, setValue] = useState<T | Array<T> | null>(null)
  const [error, setError] = useState<E | Response | null>(null)
  const [pending, setPending] = useState(false)

  const execute = useCallback(async (params?) => {
    setPending(true)
    try {
      const response: Response = await asyncFn(params)
      if (!response.ok) {
        setError(response)
        return
      }

      setValue(await response?.json())
    } catch (err) {
      setError(err)
    }
  }, [asyncFn])

  useEffect(() => {
    if (immediate) {
      ;(async () => await execute())()
    }
  }, [execute, immediate])

  return { execute, value, pending, error }
}

export default useAsync
 

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

components/InfiniteScroll.tsx

 type InfiniteScrollProps<T> = {
  items: Array<T>
  renderItem: (
    itemProps: {
      item: T
      idx: number
      array: Array<T>
      key: string | number
    },
  ) => React.ReactNode
  renderEmptyList?: () => React.ReactNode
  keyExtractor: (item: T, idx: number) => string
  className?: string
}

function InfiniteScroll<T>({
  items,
  keyExtractor,
  renderItem,
  renderEmptyList,
  className
}: InfiniteScrollProps<T>) {
  console.log({items})
  return (
    <div className={`${className ? className: ''} infinite-scroll`}>
      {renderEmptyList amp;amp; !items?.length amp;amp; renderEmptyList()}
      {items?.map((item, idx, array) => {
        return renderItem({ item, idx, array, key: keyExtractor(item, idx) })
      })}
    </div>
  )
}

export default InfiniteScroll
 

Who know how to fix this error ?

================================

Дополнительный вопрос после исправленной ошибки выше:

У меня есть новый вопрос о приведении типов для строгого типа. Теперь я реорганизовываю код функции api и использую синхронизацию, которая приведена ниже… но у меня все еще та же ошибка, если я не использую as ключевое слово

 <InfiniteScroll
  items={countries as Array<Country>}
/>
 

services/ countries.api.ts

 import axios from 'axios'

export async function findAll(params?: {
  searchField?: string
  region?: string
}) {
  if (params?.searchField || params?.region) {
    return await axios.get(`${BASE_URL}/name/${params.searchField}`)
  }
  return await axios.get(`${BASE_URL}/all`)
}

 

hooks/useAsync.tsx

(Я уже привел строгий тип, чтобы быть Array<T> | T перед возвращаемым значением из этого хука)

 function useAsync<T, E>(
  asyncFn: (params?: any) => Promise<any>,
  immediate: boolean = true
) {
  const [value, setValue] = useState<T | Array<T> | null>(null)
  const [error, setError] = useState<E | null>(null)
  const [pending, setPending] = useState(false)

  const execute = useCallback(
    async (params?) => {
      setPending(true)
      try {
        // Type casting this line before return value from this hook
        const { data }: { data: Array<T> | T } = await asyncFn(params)
        setValue(data)
      } catch (err) {
        setError(err)
        console.error('❌ Error', err.message)
      }
    },
    [asyncFn]
  )

  useEffect(() => {
    if (immediate) {
      ;(async () => await execute())()
    }
  }, [execute, immediate])

  return { execute, value, pending, error }
}
 

Ответ №1:

Вам нужно привести к любому, а затем присвоить его типу

 const  variable = countries as any as yourtypearray
 

Комментарии:

1. Это сработало … и я чувствую, что он взламывает листья. у вас есть способ решить эту проблему в корне?

2. Я имел в виду, когда другой разработчик использует мой InfiniteScroll компонент. Они должны взламывать так каждый раз. <InfiniteScroll items={countries as any as Array<Country>} />

3. Да, ваша функция api findall возвращает any или unknown, вы можете преобразовать свой массив в strong type array для этой функции.

4. У меня есть новый вопрос по почте. можете ли вы ответить или предложить больше?

5. Хорошо, вам нужно изменить метод api на общий тип, который вернет T[] . дайте мне знать, если вам нужен пример кода.