diff --git a/src/index.ts b/src/index.ts index 90d2b550..30cceccf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,23 +21,25 @@ export * from '@supabase/realtime-js' export { default as SupabaseClient } from './SupabaseClient' export type { SupabaseClientOptions, QueryResult, QueryData, QueryError } from './lib/types' -/** - * Creates a new Supabase Client. - */ -export const createClient = < +export type CreateClientHelper = < Database = any, SchemaName extends string & keyof Database = 'public' extends keyof Database ? 'public' : string & keyof Database, - Schema extends GenericSchema = Database[SchemaName] extends GenericSchema - ? Database[SchemaName] - : any + Schema = Database[SchemaName] extends GenericSchema ? Database[SchemaName] : any >( supabaseUrl: string, supabaseKey: string, - options?: SupabaseClientOptions -): SupabaseClient => { - return new SupabaseClient(supabaseUrl, supabaseKey, options) + options?: SupabaseClientOptions & AdditionalOptions +) => SupabaseClient + +export type GenericSupabaseClient = SupabaseClient + +/** + * Creates a new Supabase Client. + */ +export const createClient: CreateClientHelper = (supabaseUrl, supabaseKey, options) => { + return new SupabaseClient(supabaseUrl, supabaseKey, options) } // Check for Node.js <= 18 deprecation diff --git a/src/lib/types.ts b/src/lib/types.ts index eaafd889..0cecceb1 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -123,3 +123,4 @@ export type GenericSchema = { export type QueryResult = T extends PromiseLike ? U : never export type QueryData = T extends PromiseLike<{ data: infer U }> ? Exclude : never export type QueryError = PostgrestError +export type ServicesOptions = {} diff --git a/test/integration/next/app/layout.tsx b/test/integration/next/app/layout.tsx new file mode 100644 index 00000000..0cc4b8ea --- /dev/null +++ b/test/integration/next/app/layout.tsx @@ -0,0 +1,14 @@ +import type { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'Supabase Integration Test', + description: 'Testing Supabase integration with Next.js', +} + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ) +} diff --git a/test/integration/next/package.json b/test/integration/next/package.json index 6ee2dc94..374cbda1 100644 --- a/test/integration/next/package.json +++ b/test/integration/next/package.json @@ -7,7 +7,8 @@ "lint": "next lint", "test": "playwright test", "test:ui": "playwright test --ui", - "test:debug": "playwright test --debug" + "test:debug": "playwright test --debug", + "test:types": "npx tsd --files tests/types/*.test-d.ts" }, "dependencies": { "@radix-ui/react-checkbox": "^1.3.1", @@ -37,6 +38,7 @@ "postcss": "^8", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", + "tsd": "^0.33.0", "typescript": "^5" } } diff --git a/test/integration/next/tests/types/types.test-d.ts b/test/integration/next/tests/types/types.test-d.ts new file mode 100644 index 00000000..db0ab4e5 --- /dev/null +++ b/test/integration/next/tests/types/types.test-d.ts @@ -0,0 +1,119 @@ +import { createServerClient, createBrowserClient } from '@supabase/ssr' +import { expectType } from 'tsd' + +// Copied from ts-expect +// https://github.com/TypeStrong/ts-expect/blob/master/src/index.ts#L23-L27 +export type TypeEqual = (() => T extends Target ? 1 : 2) extends < + T +>() => T extends Value ? 1 : 2 + ? true + : false + +type Database = { + public: { + Tables: { + shops: { + Row: { + address: string | null + id: number + shop_geom: unknown | null + } + Insert: { + address?: string | null + id: number + shop_geom?: unknown | null + } + Update: { + address?: string | null + id?: number + shop_geom?: unknown | null + } + Relationships: [] + } + } + Views: { + [_ in never]: never + } + Functions: { + [_ in never]: never + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } +} + +{ + // createBrowserClient should return a typed client + const pg12Client = createBrowserClient('HTTP://localhost:3000', '') + const res12 = await pg12Client.from('shops').select('*') + expectType< + TypeEqual< + | { + address: string | null + id: number + shop_geom: unknown | null + }[] + | null, + typeof res12.data + > + >(true) +} + +{ + // createBrowserClient should infer everything to any without types provided + const pg12Client = createBrowserClient('HTTP://localhost:3000', '') + const res12 = await pg12Client.from('shops').select('address, id, relation(field)') + expectType< + TypeEqual< + | { + address: any + id: any + relation: { + field: any + }[] + }[] + | null, + typeof res12.data + > + >(true) +} + +{ + // createServerClient should return a typed client + const pg12Server = createServerClient('HTTP://localhost:3000', '') + const res12 = await pg12Server.from('shops').select('*') + expectType< + TypeEqual< + | { + address: string | null + id: number + shop_geom: unknown | null + }[] + | null, + typeof res12.data + > + >(true) +} + +{ + // createServerClient should infer everything to any without types provided + const pg12Server = createServerClient('HTTP://localhost:3000', '') + const res12 = await pg12Server.from('shops').select('address, id, relation(field)') + expectType< + TypeEqual< + | { + address: any + id: any + relation: { + field: any + }[] + }[] + | null, + typeof res12.data + > + >(true) +} diff --git a/test/integration/next/tsconfig.json b/test/integration/next/tsconfig.json index ac0369a8..32d8de30 100644 --- a/test/integration/next/tsconfig.json +++ b/test/integration/next/tsconfig.json @@ -24,5 +24,5 @@ } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "test/types/*.test-d.ts"] }