samedi 21 octobre 2023

Compound with Render Props pattern TypeScript issue

I defined BodyProps type in Table.Body component where I know for now that I will receive two interfaces of data - Cabin and CabinReservation. I defined in BodyProps, data as a union of those two interfaces as I assumed it would guarantee safety and as the Compound component is highly reusable it will accept those two interfaces in different situations without any problems. Now in the BookingTable when I'm passing into render prop pattern how I would display the passed data using BookingRow component where inside of it I defined its BookingRowProps type interface where booking is assigned to CabinReservation as I will only use this data here, it spits error in previously mentioned render that passed booking prop is Type 'CabinReservation | Cabin' is not assignable to type 'CabinReservation'.. Do I need to also define a union in BookingRow to pass the issue or is there a different, better approach? I still learning best practices in defining types with TypeScript so every piece of advice will be appreciated.

Type 'Cabin | CabinReservation' is not assignable to type 'CabinReservation'.
  Type 'Cabin' is missing the following properties from type 'CabinReservation': startDate, endDate, numNights, numGuests, and 4 more.ts(2322)

BookingRow component

type BookingRowProps = {
  booking: CabinReservation;
};

function BookingRow({
  booking: {
    id: bookingId,
    created_at,
    startDate,
    endDate,
    numNights,
    numGuests,
    totalPrice,
    status,
    guests: { fullName: guestName, email },
    cabins: { name: cabinName },
  },
}: BookingRowProps) {
  const statusToTagName = {
    unconfirmed: 'blue',
    'checked-in': 'green',
    'checked-out': 'silver',
  };

  return (
    <Table.Row>
      <Cabin>{cabinName}</Cabin>

      <Stacked>
        <span>{guestName}</span>
        <span>{email}</span>
      </Stacked>

      <Stacked>
        <span>
          {isToday(new Date(startDate))
            ? 'Today'
            : formatDistanceFromNow(startDate)}{' '}
          &rarr; {numNights} night stay
        </span>
        <span>
          {format(new Date(startDate), 'MMM dd yyyy')} &mdash;{' '}
          {format(new Date(endDate), 'MMM dd yyyy')}
        </span>
      </Stacked>

      <Tag type={statusToTagName[status]}>{status.replace('-', ' ')}</Tag>

      <Amount>{formatCurrency(totalPrice)}</Amount>
    </Table.Row>
  );
}

export default BookingRow;

BookingTable component

function BookingTable() {
  const { bookings, isLoading } = useBookings();

  if (isLoading) return <Spinner />;
  if (bookings.length === 0) return <Empty resourceName="bookings" />;

  return (
    <Menus>
      <Table columns="0.6fr 2fr 2.4fr 1.4fr 1fr 3.2rem">
        <Table.Header>
          <div>Cabin</div>
          <div>Guest</div>
          <div>Dates</div>
          <div>Status</div>
          <div>Amount</div>
          <div></div>
        </Table.Header>

        <Table.Body
          data={bookings}
          render={booking => <BookingRow key={booking.id} booking={booking} />}
        />
      </Table>
    </Menus>
  );
}

Table Compound component

type StyledCommonRow = {
  columns: string | null;
};

type TableProps = {
  columns: string;
  children: React.ReactNode;
};

type HeaderProps = {
  children: React.ReactNode;
};

type RowProps = {
  children: React.ReactNode;
};

type BodyProps = {
  data: Cabin[] | CabinReservation[];
  render: (data: Cabin | CabinReservation) => React.ReactNode;
};

export default function Table({ columns, children }: TableProps) {
  return (
    <TableContext.Provider value=>
      <StyledTable role="table">{children}</StyledTable>
    </TableContext.Provider>
  );
}

function Header({ children }: HeaderProps) {
  const { columns } = useTableContext();

  return (
    <StyledHeader role="row" columns={columns} as="header">
      {children}
    </StyledHeader>
  );
}
function Row({ children }: RowProps) {
  const { columns } = useTableContext();

  return (
    <StyledRow role="row" columns={columns}>
      {children}
    </StyledRow>
  );
}

function Body({ data, render }: BodyProps) {
  return <StyledBody>{data.map(render)}</StyledBody>;
}

Aucun commentaire:

Enregistrer un commentaire