網站搜尋

reactjs - 使用 TypeScript 的 React FlatList


TypeScript 真的是非常棒,只是目前我覺得我為 TypeScript 工作的感覺,比 TypeScript 為我工作的感覺還要多。

我有一個 FlatList,它在旋轉木馬中呈現餐廳結果。

 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 對 item 提出警告,顯示 Binding element 'item' implicitly has an 'any' type — 有道理。所以我嘗試告訴它應該期望什麼:

  interface RestaurantItem {
    item: {
      id: string;
      name: string;
    };
  }
  const renderRestaurantRows = ({ item }: RestaurantItem) => (
    <Carousel
      id={item.id}
      name={item.name}
    />
  );

但是接著 TS 對 data={restaurants] 提出抱怨,顯示 Type 'Restaurant[]' is not assignable to type 'readonly { id: string; name: string; imageUrl: string; rating: number; reviews: number; }',還有大量其他資訊。

有人能分享一個解決方案並且解釋將來如何在其他類似情況下找到這種解決方案嗎?

更新

restaurants 是從自定義 Hook 中獲取的。它被定義為 Restaurant 對象的數組:

interface Restaurant {
  id: string;
  name: string;
}
export default function useRestaurantSearch() {
  const [restaurants, setRestaurants] = useState<Restaurant[] | null>(null);
  ...

更新 2

大家謝謝你們的評論,根據這兩個建議,我重寫了程式碼:

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>
  );

這次我在 { item }: Restaurant 出現了錯誤,提示 Cannot find name 'Restaurant'。這並不奇怪,因為 Restaurant 定義在外部的 hooks 文件 ../hooks/useRestaurantSearch 中。我是否需要以某種方式導入它?

更新 3

經過好幾個小時的嘗試後,我到了這個地步:

  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 生成一個物件,該物件包含每個餐廳的資料,並將其放在 item 鍵下。例如,其中一個物件可能是:

{
  "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],
  },
}

當我傳遞一個 result 物件時,我可以讓 TS 知道物件內的 item 並將其轉型為 Restaurant 類型。我是用 (result: { item: Restaurant }) 來做的。然而,當我嘗試直接用({ item }: Restaurant)解構result物件時,它會報錯Property 'item' does not exist on type 'Restaurant'`。有人知道為什麼嗎?

答案#1

嘗試導入 ListRenderItem 並為接收該函數的常數指定類型:

import { ListRenderItem } from 'react-native';
...
const renderRestaurantRows: ListRenderItem<Restaurant> = ({ item }) => (
  <Carousel
    id={item.id}
    name={item.name}
  />
);

從 renderItems 真正傳入作為參數的是:

其中 ItemT 是你的類型,Restaurant。