I encountered error TS2322: Type 'unknown' is not assignable to type 'Country[]'
https://i.sstatic.net/O4gUu.png
pages/Countries/index.tsx
Full code:
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"
/>
)
}
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
These are three files that I think according.
services/APIs/countries.api.ts
(API from )
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}`)
}
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
useAsync.tsx
(this hook implement to handle success and error of async function)
Full code:
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 && !items?.length && 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 ?
================================
Addition question after fixed above error:
I have a new question about type casting for strong type.
Now, I refactor code of api function and useAsync hook to be below...
but I still have the same error if I don't use an as
keyword
<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
(I have already casted strong type to be Array<T> | T
before return value from this hook)
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 }
}