TypeScript is really great, just for the moment I feel like I work for TypeScript more than TypeScript works for me.
I have a FlatList that renders restaurant results in a Carousel.
const renderRestaurantRows = ({ item }) => ( <Carousel id={item.id} name={item.name} /> ); const renderBottomSheetRestaurantList = () => ( <View style={[styles.listView, { height: topSnapPoint }]}> <FlatList data={restaurants} keyExtractor={(item) => `row-${item.id}`} renderItem={renderRestaurantRows} /> </View> );
TypeScript complains about item
with Binding element 'item' implicitly has an 'any' type
– makes sense. So I try to tell it what to expect:
interface RestaurantItem { item: { id: string; name: string; }; } const renderRestaurantRows = ({ item }: RestaurantItem) => ( <Carousel id={item.id} name={item.name} /> );
But then TS complains about data={restaurants]
with Type 'Restaurant[]' is not assignable to type 'readonly { id: string; name: string; imageUrl: string; rating: number; reviews: number; }
and a ton of other information.
Any chance someone can share a solution AND explain how to find such a solution in the future for other similar cases?
UPDATE
restaurants
is fetched from a custom hook. It is defined as an array of Restaurant objects:
interface Restaurant { id: string; name: string; } export default function useRestaurantSearch() { const [restaurants, setRestaurants] = useState<Restaurant[] | null>(null); ...
UPDATE 2
Guys thanks for the comments, following the two suggestions, I rewrote the code:
import useRestaurantSearch from '../hooks/useRestaurantSearch'; export default function RestaurantPage() { const renderRestaurantRows = ({ item }: Restaurant) => ( <Carousel id={item.id} name={item.name} /> ); const renderBottomSheetRestaurantList = () => ( <View style={[styles.listView, { height: topSnapPoint }]}> <FlatList data={restaurants} keyExtractor={(item) => `row-${item.id}`} renderItem={renderRestaurantRows} /> </View> );
This time I am getting an error for { item }: Restaurant
with Cannot find name 'Restaurant'
. And it’s not surprising because Restaurant is defined in an external hooks file ../hooks/useRestaurantSearch
. Do I need to import it somehow?
UPDATE 3
After hours of playing around with this, I got to this point:
const renderRestaurantRows = (result: { item: Restaurant }) => { return <Carousel {...result.item} />; }; const renderBottomSheetRestaurantList = () => ( <View style={[styles.listView, { height: topSnapPoint }]}> <FlatList data={restaurants} keyExtractor={(item) => `row-${item.id}`} renderItem={renderRestaurantRows} /> </View> );
renderItem
generates an object which contains each restaurant data under an item
key. So for example, one of the objects could be:
{ "index": 18, "item": Object { "coordinates": Object { "latitude": 123, "longitude": -123, }, "id": "dfg987fshjsdfh", "name": "Yummy Food", }, "separators": Object { "highlight": [Function highlight], "unhighlight": [Function unhighlight], "updateProps": [Function updateProps], }, }
When I pass a result
object, I can let TS know about item
inside the object and casting it to the Restaurant type. I do it with (result: { item: Restaurant }). However, when I try to directly destructure the
resultobject with
({ item }: Restaurant)it gives me the error
Property ‘item’ does not exist on type ‘Restaurant’`. Any idea why?
Answer
Try importing ListRenderItem and type the const receiving the function:
import { ListRenderItem } from 'react-native'; ... const renderRestaurantRows: ListRenderItem<Restaurant> = ({ item }) => ( <Carousel id={item.id} name={item.name} /> );
What you’re really getting as arguments from the renderItems is:
Where ItemT
is your type, Restaurant
.