Home > OS >  React Typescript: Problem with React.Key when item[keyProp]
React Typescript: Problem with React.Key when item[keyProp]

Time:09-12

I would like to make a general component that render a list. I am having trouble when i have to pass the key. I thought I was passing the property name which is to be taken as a key, but I get an error

import { Swiper, SwiperSlide } from "swiper/react"

interface HorizontalSectionProps<T> {
  // Title of the section
  title: string
  // Items that render each element of the section
  card: (args0: T) => React.ReactNode
  // Items to show
  items: T[]
  // Property that is a key
  keyProp: keyof T

  // Loading state
  isLoading?: boolean
}

/**
 * This component is used to display a horizontal section with a title and a list of items
 * For mobile there is an horizontal scrolling section
 * For tablet  there is grids of cards displayed in max 2 rows
 * For desktop there is grids of cards displayed in one 1 row
 * @param {HorizontalSectionProps} props
 */
export default function HorizontalSection<T>({
  title,
  card,
  items,
  keyProp,
  isLoading = false,
}: HorizontalSectionProps<T>) {
  return (
    <>
      <h2 className="text-2xl sm:text-3xl mb-6">{title}</h2>

      {isLoading ? (
        <>Loading</>
      ) : (
        <>
          {/* ===== MOBILE HORIZONTAL SECTION ===== */}
          <Swiper
            slidesPerView={2}
            // centeredSlides={true}
            spaceBetween={10}
            loop={true}
            grabCursor={true}
            pagination={{
              clickable: true,
              type: "bullets",
            }}
            className="mySwiper card-swiper sm:hidden"
          >
            {items?.map((item, index) => (
                //TODO: Here I have the error on key property
              <SwiperSlide key={item[keyProp]}>{card(item)}</SwiperSlide>
            ))}
          </Swiper>

          {/* ===== DESKTOP HORIZONTAL SECTION ===== */}
          <div className="hidden sm:grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-x-4 gap-y-10 sm:gap-y-6 mb-10">
            {items?.map((item) => card(item))}
          </div>
        </>
      )}
    </>
  )
}

I get this error:

Type 'T[keyof T]' is not assignable to type 'Key | null | undefined'.
  Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'Key | null | undefined'.
    Type 'T[string]' is not assignable to type 'Key | null | undefined'.ts(2322)

EDIT: I added the way i use the component, so you can understand better. Inside card i pass the component that needs to be rendered for each item and inside keyProp I wanted to pass the name of the property that acts as a key

        <div className="recSection my-12 app-max-width app-x-padding">
          <HorizontalSection<Product>
            title={t("you_may_also_like")}
            card={(item: Product) => <Card key={item.nodo} item={item} />}
            items={suggestedProducts}
            keyProp="nodo"
          />
        </div>

CodePudding user response:

Problem

You didn't include the example props, but let say that T is a string

Now keyof T is something like: keyProp: number | typeof Symbol.iterator | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | ... 30 more ... | "padEnd"

Solution

You probably meant:

keyof T[];

CodePudding user response:

In the cases like this, you need to have an abstraction based on your key:

interface HorizontalSectionProps<K extends string, T extends Record<K, string | number>> {
  // Title of the section
  title: string
  // Items that render each element of the section
  card: (args0: T) => ReactNode
  // Items to show
  items: T[]
  // Property that is a key
  keyProp: keyof T

  // Loading state
  isLoading?: boolean
}

Then you'll need to change your component to look like this:

export default function HorizontalSection<K extends string, T extends Record<K, string | number>>({
  title,
  card,
  items,
  keyProp,
  isLoading = false,
}: HorizontalSectionProps<K, T>) {

Then it'll assume that T[K] is a string or a number.

See the example here

  • Related