Search code examples
arraystypescriptnext.jstypessupabase

Types for the data from supabase


1

I'm trying define the types for the data from supabse I cannot and I got the error message.

"type '{ id: string; title: string; capacity: number | null; start_date: Date | null; start_time: string | null; end_date: Date | null; end_time: string | null ; image_url: string | null; event_participate: { ... ; }[]; }[] | null' cannot be assigned to type 'Event[]'. Cannot assign type 'null' to type 'Event[]'."

I've followed the official document below but but I haven't found the solution yet. Could you tell me how I can define the Array from supabse? https://supabase.com/docs/reference/javascript/typescript-support

I would be very grateful if someone could point me in the right direction.

Here are the codes.

#event-index.tsx

"use client";
import Link from "next/link";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Typography from "@mui/material/Typography";
import { Container, CardMedia, Stack } from "@mui/material";
import GlobalHeader from "@/components/globalHeader";
import format from "date-fns/format";
import { ReactNode } from "react";

interface Event {
  id: string;
  title: string;
  capacity: number;
  start_date: Date;
  start_time: string;
  end_date: Date;
  end_time: string;
  image_url: string;
  event_participate: {
    length: ReactNode;
    id: string;
  };
}

type TypedEvent = {
  eventList: Array<Event>;
};

const EventIndex: React.FC<TypedEvent> = ({ eventList }) => {
  const events = eventList && eventList;

  return (
    <>
      <div>
        <div>
          <GlobalHeader />
        </div>
        <div>
          <Container component="main" fixed sx={{ mt: 10 }}>
            <Stack direction="row" spacing={2}>
              {events &&
                events.map((event: Event) => (
                  <div key={event.id}>
                    <Card sx={{ maxWidth: 345 }}>
                      <div>
                        {" "}
                        <CardMedia
                          component="img"
                          sx={{ height: 140 }}
                          image={event.image_url}
                          alt="イベントの表紙"
                        />
                      </div>
                      <CardContent>
                        <Typography gutterBottom variant="h5" component="div">
                          <Link href="/event/[id]" as={`/event/${event.id}`}>
                            {event.title}
                          </Link>
                        </Typography>
                        <Typography
                          variant="body2"
                          color="text.secondary"
                          component="div"
                        >
                          <div>
                            {format(new Date(event.start_date), "yyyy/MM/dd")}
                            {event.start_time}-{" "}
                            {format(new Date(event.end_date), "yyyy/MM/dd")}
                            {event.end_time}
                          </div>
                        </Typography>
                        <Typography
                          variant="body2"
                          color="text.secondary"
                          component="div"
                        >
                          参加者数:{event.event_participate.length}/
                          {event.capacity}
                        </Typography>
                      </CardContent>
                    </Card>
                  </div>
                ))}
            </Stack>
            <div>
              {" "}
              <Link href="/event-input">イベントを企画する</Link>
            </div>
          </Container>
        </div>
      </div>
    </>
  );
};

export default EventIndex;

#page.tsx

import EventIndex from "./event-index";
import { supabase } from "@/utils/supabase";
import { ReactNode } from "react";

interface Event {
  id: string;
  title: string;
  capacity: number;
  start_date: Date;
  start_time: string;
  end_date: Date;
  end_time: string;
  image_url: string;
  event_participate: {
    length: ReactNode;
    id: string;
  };
}
const Event = async () => {
  type DbResult<T> = T extends PromiseLike<infer U> ? U : never;
  const query = supabase
    .from("events")
    .select(
      `
id,title,capacity,start_date,start_time,end_date,end_time,image_url,
event_participate ( id )`
    )
    .eq("is_published", true);
  const events: DbResult<typeof query> = await query;
  return (
    <>
      <EventIndex eventList={events && events.data} />
    </>
  );
};

export default Event;


#database.types.ts

export type Json =
  | string
  | number
  | boolean
  | null
  | { [key: string]: Json | undefined }
  | Json[];

export interface Database {
  public: {
    Tables: {
      event_participate: {
        Row: {
          id: number;
          participated_event_id: string | null;
          participating_account_id: string | null;
        };
        Insert: {
          id?: number;
          participated_event_id?: string | null;
          participating_account_id?: string | null;
        };
        Update: {
          id?: number;
          participated_event_id?: string | null;
          participating_account_id?: string | null;
        };
        Relationships: [
          {
            foreignKeyName: "event_participate_participated_event_id_fkey";
            columns: ["participated_event_id"];
            referencedRelation: "events";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "event_participate_participating_account_id_fkey";
            columns: ["participating_account_id"];
            referencedRelation: "profiles";
            referencedColumns: ["id"];
          }
        ];
      };
      events: {
        Row: {
          capacity: number | null;
          description: string;
          end_date: Date | null;
          end_time: string | null;
          host_id: string | null;
          id: string;
          image_url: string | null;
          is_published: boolean | null;
          place: string | null;
          place_link: string | null;
          start_date: Date | null;
          start_time: string | null;
          title: string;
        };
        Insert: {
          capacity?: number | null;
          description: string;
          end_date?: Date | null;
          end_time?: string | null;
          host_id?: string | null;
          id?: string;
          image_url?: string | null;
          is_published?: boolean | null;
          place?: string | null;
          place_link?: string | null;
          start_date?: Date | null;
          start_time?: string | null;
          title: string;
        };
        Update: {
          capacity?: number | null;
          description?: string;
          end_date?: Date | null;
          end_time?: string | null;
          host_id?: string | null;
          id?: string;
          image_url?: string | null;
          is_published?: boolean | null;
          place?: string | null;
          place_link?: string | null;
          start_date?: Date | null;
          start_time?: string | null;
          title?: string;
        };
        Relationships: [
          {
            foreignKeyName: "events_host_id_fkey";
            columns: ["host_id"];
            referencedRelation: "profiles";
            referencedColumns: ["id"];
          }
        ];
      };
      profiles: {
        Row: {
          avatar_url: string | null;
          full_name: string | null;
          id: string;
          updated_at: string | null;
          username: string | null;
          website: string | null;
        };
        Insert: {
          avatar_url?: string | null;
          full_name?: string | null;
          id: string;
          updated_at?: string | null;
          username?: string | null;
          website?: string | null;
        };
        Update: {
          avatar_url?: string | null;
          full_name?: string | null;
          id?: string;
          updated_at?: string | null;
          username?: string | null;
          website?: string | null;
        };
        Relationships: [
          {
            foreignKeyName: "profiles_id_fkey";
            columns: ["id"];
            referencedRelation: "users";
            referencedColumns: ["id"];
          }
        ];
      };
    };
    Views: {
      [_ in never]: never;
    };
    Functions: {
      [_ in never]: never;
    };
    Enums: {
      [_ in never]: never;
    };
    CompositeTypes: {
      [_ in never]: never;
    };
  };
}

export type Tables<T extends keyof Database["public"]["Tables"]> =
  Database["public"]["Tables"][T]["Row"];

Solution

  • You shouldn't define your own types like this.

    interface Event {
      id: string;
      title: string;
      capacity: number;
      start_date: Date;
      start_time: string;
      end_date: Date;
      end_time: string;
      image_url: string;
      event_participate: {
        length: ReactNode;
        id: string;
      };
    }
    

    Instead, just use the types that Supabase has generated for you:

    type Event = Database['public']['Tables']['events']['Row']
    

    The code is not verified, but hopefully you get the idea.