diff --git a/src/select-query-parser.ts b/src/select-query-parser.ts
index 5c682117..54f8f317 100644
--- a/src/select-query-parser.ts
+++ b/src/select-query-parser.ts
@@ -129,54 +129,91 @@ type EatWhitespace = string extends Input
: Input
/**
- * Returns a boolean representing whether there is a foreign key with the given name.
+ * Check if Item is in Array
*/
-type HasFKey = Relationships extends [infer R]
- ? R extends { foreignKeyName: FKeyName }
+type InArray- = Array extends [infer I]
+ ? I extends Item
? true
: false
- : Relationships extends [infer R, ...infer Rest]
- ? HasFKey extends true
+ : Array extends [infer I, ...infer Rest]
+ ? InArray
- extends true
? true
- : HasFKey
+ : InArray
-
: false
+/**
+ * Returns a boolean representing whether there is a foreign key with the given name.
+ */
+type HasFKey = InArray<{ foreignKeyName: FKeyName }, Relationships>
/**
* Returns a boolean representing whether there the foreign key has a unique constraint.
*/
-type HasUniqueFKey = Relationships extends [infer R]
- ? R extends { foreignKeyName: FKeyName; isOneToOne: true }
- ? true
- : false
+type HasUniqueFKey = InArray<
+ { foreignKeyName: FKeyName; isOneToOne: true },
+ Relationships
+>
+
+/**
+ * Returns the relation referenced by this column name, if such relation exists
+ */
+type ColumnForeignRelation = Relationships extends [infer R]
+ ? R extends { columns: string[]; referencedRelation: string }
+ ? InArray extends true
+ ? [R['referencedRelation']]
+ : null
+ : null
: Relationships extends [infer R, ...infer Rest]
- ? HasUniqueFKey extends true
- ? true
- : HasUniqueFKey
- : false
+ ? ColumnForeignRelation extends [infer Rel]
+ ? [Rel]
+ : ColumnForeignRelation
+ : null
/**
* Returns a boolean representing whether there is a foreign key referencing
* a given relation.
*/
-type HasFKeyToFRel = Relationships extends [infer R]
- ? R extends { referencedRelation: FRelName }
- ? true
- : false
- : Relationships extends [infer R, ...infer Rest]
- ? HasFKeyToFRel extends true
- ? true
- : HasFKeyToFRel
- : false
+type HasFKeyToFRel = InArray<
+ { referencedRelation: FRelName },
+ Relationships
+>
-type HasUniqueFKeyToFRel = Relationships extends [infer R]
- ? R extends { referencedRelation: FRelName; isOneToOne: true }
- ? true
- : false
- : Relationships extends [infer R, ...infer Rest]
- ? HasUniqueFKeyToFRel extends true
- ? true
- : HasUniqueFKeyToFRel
- : false
+type HasUniqueFKeyToFRel = InArray<
+ { referencedRelation: FRelName; isOneToOne: true },
+ Relationships
+>
+
+type FieldRelation<
+ Schema extends GenericSchema,
+ Field extends { name: string; children: unknown[] },
+ FRel extends keyof (Schema['Tables'] & Schema['Views']),
+ RelationName,
+ Relationships
+> = {
+ [_ in Field['name']]: GetResultHelper<
+ Schema,
+ (Schema['Tables'] & Schema['Views'])[FRel]['Row'],
+ FRel,
+ (Schema['Tables'] & Schema['Views'])[FRel] extends { Relationships: infer R } ? R : unknown,
+ Field['children'],
+ unknown
+ > extends infer Child
+ ? // One-to-one relationship - referencing column(s) has unique/pkey constraint.
+ HasUniqueFKeyToFRel<
+ RelationName,
+ (Schema['Tables'] & Schema['Views'])[FRel] extends {
+ Relationships: infer R
+ }
+ ? R
+ : unknown
+ > extends true
+ ? Child | null
+ : Relationships extends unknown[]
+ ? HasFKeyToFRel extends true
+ ? Child | null
+ : Child[]
+ : Child[]
+ : never
+}
/**
* Constructs a type definition for a single field of an object.
@@ -237,34 +274,13 @@ type ConstructFieldDefinition<
: never
}
: Field extends { name: string; original: string; children: unknown[] }
- ? {
- [_ in Field['name']]: GetResultHelper<
- Schema,
- (Schema['Tables'] & Schema['Views'])[Field['original']]['Row'],
- Field['original'],
- (Schema['Tables'] & Schema['Views'])[Field['original']] extends { Relationships: infer R }
- ? R
- : unknown,
- Field['children'],
- unknown
- > extends infer Child
- ? // One-to-one relationship - referencing column(s) has unique/pkey constraint.
- HasUniqueFKeyToFRel<
- RelationName,
- (Schema['Tables'] & Schema['Views'])[Field['original']] extends {
- Relationships: infer R
- }
- ? R
- : unknown
- > extends true
- ? Child | null
- : Relationships extends unknown[]
- ? HasFKeyToFRel extends true
- ? Child | null
- : Child[]
- : Child[]
- : never
- }
+ ? ColumnForeignRelation extends [infer ForeignRel]
+ ? // handle `col:foreign_key_column`
+ ForeignRel extends keyof (Schema['Tables'] & Schema['Views'])
+ ? FieldRelation
+ : SelectQueryError<`Unknown relation in a relationship`>
+ : // handle `col:relation`
+ FieldRelation
: Field extends { name: string; type: infer T }
? { [K in Field['name']]: T }
: Field extends { name: string; original: string }
diff --git a/test/index.test-d.ts b/test/index.test-d.ts
index b9c24c16..17adf0b2 100644
--- a/test/index.test-d.ts
+++ b/test/index.test-d.ts
@@ -149,6 +149,30 @@ const postgrest = new PostgrestClient(REST_URL)
expectType(message.user)
}
+// many-to-one relationship (fkey)
+{
+ const { data: message, error } = await postgrest
+ .from('messages')
+ .select('user:users!messages_username_fkey(*)')
+ .single()
+ if (error) {
+ throw new Error(error.message)
+ }
+ expectType(message.user)
+}
+
+// many-to-one relationship (column name)
+{
+ const { data: message, error } = await postgrest
+ .from('messages')
+ .select('user:username(*)')
+ .single()
+ if (error) {
+ throw new Error(error.message)
+ }
+ expectType(message.user)
+}
+
// one-to-many relationship
{
const { data: user, error } = await postgrest.from('users').select('messages(*)').single()