如何结合Supabase生成的数据库类型使用TypeScript泛型?
问题:Supabase泛型Insert类型不兼容错误
实现代码
type DbTables = Database['public']['Tables']; type TableName = keyof DbTables; type DbInsert<T extends TableName> = DbTables[T]['Insert']; async function create<T extends TableName>(insertData: DbInsert<T> | DbInsert<T>[], resourceName: T) { try { const { data, error } = await supabase.from(resourceName).insert(insertData).select(); if (error) throw error; return data; } catch (error) { console.error(error); } }
数据库表结构
create table if not exists public.foo ( id bigint unique generated always as identity, bar text not null, baz real [] not null, qux jsonb not null );
生成的类型定义
export type Json = string | number | boolean | null | { [key: string]: Json } | Json[]; export interface Database { public: { Tables: { foo: { Row: { bar: string; baz: number[]; id: number; qux: Json; }; Insert: { bar: string; baz: number[]; id?: never; qux: Json; }; Update: { bar?: string; baz?: number[]; id?: never; qux?: Json; }; }; }; Views: { [_ in never]: never; }; Functions: { [_ in never]: never; }; Enums: { [_ in never]: never; }; }; }
错误信息(翻译后)
类型“DbInsert<T> | DbInsert<T>[]”无法分配给参数类型“({ foo: { Row: { bar: string; baz: number[]; id: number; qux: Json; }; Insert: { bar: string; baz: number[]; id?: undefined; qux: Json; }; Update: { bar?: string | undefined; baz?: number[] | undefined; id?: undefined; qux?: Json | undefined; }; }; }[T] extends { ...; } ? { ...; }[T]["Insert"] : never) | ({ ...; }[T...'. 类型“DbInsert<T>”无法分配给类型“({ foo: { Row: { bar: string; baz: number[]; id: number; qux: Json; }; Insert: { bar: string; baz: number[]; id?: undefined; qux: Json; }; Update: { bar?: string | undefined; baz?: number[] | undefined; id?: undefined; qux?: Json | undefined; }; }; }[T] extends { ...; } ? { ...; }[T]["Insert"] : never) | ({ ...; }[T...'. 类型“{ bar: string; baz: number[]; id?: undefined; qux: Json; }”无法分配给类型“({ foo: { Row: { bar: string; baz: number[]; id: number; qux: Json; }; Insert: { bar: string; baz: number[]; id?: undefined; qux: Json; }; Update: { bar?: string | undefined; baz?: number[] | undefined; id?: undefined; qux?: Json | undefined; }; }; }[T] extends { ...; } ? { ...; }[T]["Insert"] : never) | ({ ...; }[T...'. 类型“{ bar: string; baz: number[]; id?: undefined; qux: Json; }”无法分配给类型“{ foo: { Row: { bar: string; baz: number[]; id: number; qux: Json; }; Insert: { bar: string; baz: number[]; id?: undefined; qux: Json; }; Update: { bar?: string | undefined; baz?: number[] | undefined; id?: undefined; qux?: Json | undefined; }; }; }[T] extends { ...; } ? { ...; }[T]["Insert"] : never'. 类型“DbInsert<T>”无法分配给类型“({ foo: { Row: { bar: string; baz: number[]; id: number; qux: Json; }; Insert: { bar: string; baz: number[]; id?: undefined; qux: Json; }; Update: { bar?: string | undefined; baz?: number[] | undefined; id?: undefined; qux?: Json | undefined; }; }; }[T] extends { ...; } ? { ...; }[T]["Insert"] : never)[]'. 类型“{ bar: string; baz: number[]; id?: undefined; qux: Json; }”缺少类型“({ foo: { Row: { bar: string; baz: number[]; id: number; qux: Json; }; Insert: { bar: string; baz: number[]; id?: undefined; qux: Json; }; Update: { bar?: string | undefined; baz?: number[] | undefined; id?: undefined; qux?: Json | undefined; }; }; }[T] extends { ...; } ? { ...; }[T]["Insert"] : never)[]”中的以下属性:length、pop、push、concat等29个属性。
解决方法
问题根源是:你定义的DbInsert<T>类型别名是泛型索引访问类型,TypeScript无法将它与Supabasefrom(resourceName).insert()方法期望的具体表Insert类型做精确匹配,泛型场景下类型推断会出现歧义。
方案1:直接使用表的Insert类型(推荐)
去掉自定义的DbInsert<T>类型别名,直接用DbTables[T]['Insert']作为参数类型,让TypeScript明确泛型与参数的绑定关系:
type DbTables = Database['public']['Tables']; type TableName = keyof DbTables; async function create<T extends TableName>(insertData: DbTables[T]['Insert'] | DbTables[T]['Insert'][], resourceName: T) { try { const { data, error } = await supabase.from(resourceName).insert(insertData).select(); if (error) throw error; return data; } catch (error) { console.error(error); } }
方案2:保留类型别名并添加约束
如果必须保留DbInsert<T>,可以给它添加条件类型约束,同时通过类型断言消除歧义:
type DbTables = Database['public']['Tables']; type TableName = keyof DbTables; // 添加条件类型,明确T是DbTables的有效键 type DbInsert<T extends TableName> = T extends keyof DbTables ? DbTables[T]['Insert'] : never; async function create<T extends TableName>(insertData: DbInsert<T> | DbInsert<T>[], resourceName: T) { try { const { data, error } = await supabase.from(resourceName).insert(insertData as DbInsert<T> | DbInsert<T>[]).select(); if (error) throw error; return data; } catch (error) { console.error(error); } }
原理说明
Supabase的from()方法会根据传入的表名返回对应表的SupabaseQueryBuilder,其insert()方法的参数是该表专属的Insert类型。直接使用DbTables[T]['Insert']时,TypeScript能将泛型T和参数类型做精确绑定,彻底消除类型歧义。
内容的提问来源于stack exchange,提问作者cohlar
相关产品推荐
相关产品推荐

