🖇️

@tanstack/react-virtual を使おうとしたら TypeScript の型推論で詰まった話

2023/02/27に公開

Reactで大量のデータをリスト表示する際に有効なバーチャルリスト用ライブラリーである @tanstack/react-virtual を使おうとしたら、TypeScript の型推論が効かなくなる壁に当たったので解決策を含めたメモをここに残します。

本題

問題となったのは以下のようなコードです。まずは型定義から。

type Fuga = {
  id: string
  name: string
  ...
}
type Piyo = {
  id: string
  name: string
  ...
}

type FugaItem = {
  type: 'fuga'
  value: Fuga
}
type PiyoItem = {
  type: 'piyo'
  value: Piyo
}

type Item = FugaItem | PiyoItem

上記の Item 型の配列を @tanstack/react-virtualuseVirtualizer() を使って、 Item.type ごとに表示する項目を出し分けたいとします。

const App = () => {
  const { data: items } = useHogeQuery()  // items の型は Item[]
  const parentRef = React.useRef()
  const rowVirtualizer = useVirtualizer({
    const : items.length,
    getScrollElement: () => scrollableNodeRef.current,
    estimateSize: () => 100,
  })

  return (
    {rowVirtualizer.getVirtualItems().map((row) => (
      <div
        key={row.key}
        data-index={row.index}
        ref={rowVirtualizer.measureElement}
      >
        {items[row.index].type === 'fuga' && <FugaRow key={items[row.index].id} fuga={items[row.index].value} />}
        {items[row.index].type === 'piyo' && <PiyoRow key={items[row.index].id} piyo={items[row.index].value} />}
      </div>
    )}
  )
}

このままだと、 items[row.index].type === ... 以降の項で、 items[row.index] の型が FugaItem なのか、PiyoItem なのかが絞り込まれず、静的解析エラーとなってしまいます。

(自分が調べた範囲ではこの現象の名前を確認することはできませんでした🥺 ご存知の方コメントいただけますと幸いです)

解決策

次のように修正することで、fuga={items[row.index].value} などの部分で型推論が効かない問題を解決できます。

const App = () => {
  ...

  return (
    {rowVirtualizer.getVirtualItems().map((row) => {
      // 配列の要素を直接参照するのではなく、変数に一度格納する
      const item = items[row.index]

      return (
        <div
          key={row.key}
          data-index={row.index}
          ref={rowVirtualizer.measureElement}
        >
          {item.type === 'fuga' && <FugaRow key={item.id} fuga={item.value} />}
          {item.type === 'piyo' && <PiyoRow key={item.id} piyo={item.value} />}
        </div>
      )
    }}
  )
}

終わりに

今回は 前回書いた記事 を踏まえた内容となりました。
毎回書くタイトルが具体的すぎるのでもう少し抽象度を上げたいのですが、それだと見返した時に何が嬉しいのか分からなくなるな〜となり悩んでます。次はもっとシンプルに書けるよう努めます。
ありがとうございました。

Discussion