Skip to content

Commit

Permalink
fix(types): overrideTypes merging with maybeSingle
Browse files Browse the repository at this point in the history
  • Loading branch information
avallete committed Feb 21, 2025
1 parent c8754b7 commit 578c69d
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 15 deletions.
15 changes: 11 additions & 4 deletions src/PostgrestBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
IsValidResultOverride,
} from './types'
import PostgrestError from './PostgrestError'
import { ContainsNull } from './select-query-parser/types'

export default abstract class PostgrestBuilder<Result, ThrowOnError extends boolean = false>
implements
Expand Down Expand Up @@ -257,14 +258,20 @@ export default abstract class PostgrestBuilder<Result, ThrowOnError extends bool
NewResult,
Options extends { merge?: boolean } = { merge: true }
>(): PostgrestBuilder<
IsValidResultOverride<Result, NewResult, true, false, false> extends true
? MergePartialResult<NewResult, Result, Options>
IsValidResultOverride<Result, NewResult, false, false> extends true
? // Preserve the optionality of the result if the overriden type is an object (case of chaining with `maybeSingle`)
ContainsNull<Result> extends true
? MergePartialResult<NewResult, NonNullable<Result>, Options> | null
: MergePartialResult<NewResult, Result, Options>
: CheckMatchingArrayTypes<Result, NewResult>,
ThrowOnError
> {
return this as unknown as PostgrestBuilder<
IsValidResultOverride<Result, NewResult, true, false, false> extends true
? MergePartialResult<NewResult, Result, Options>
IsValidResultOverride<Result, NewResult, false, false> extends true
? // Preserve the optionality of the result if the overriden type is an object (case of chaining with `maybeSingle`)
ContainsNull<Result> extends true
? MergePartialResult<NewResult, NonNullable<Result>, Options> | null
: MergePartialResult<NewResult, Result, Options>
: CheckMatchingArrayTypes<Result, NewResult>,
ThrowOnError
>
Expand Down
21 changes: 12 additions & 9 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,16 @@ type NonRecursiveType = BuiltIns | Function | (new (...arguments_: any[]) => unk
type BuiltIns = Primitive | void | Date | RegExp
type Primitive = null | undefined | string | number | boolean | symbol | bigint

export type IsValidResultOverride<Result, NewResult, Ok, ErrorResult, ErrorNewResult> =
export type IsValidResultOverride<Result, NewResult, ErrorResult, ErrorNewResult> =
Result extends any[]
? NewResult extends any[]
? // Both are arrays - valid
Ok
true
: ErrorResult
: NewResult extends any[]
? ErrorNewResult
: // Neither are arrays - valid
// Preserve the optionality of the result if the overriden type is an object (case of chaining with `maybeSingle`)
ContainsNull<Result> extends true
? Ok | null
: Ok

true
/**
* Utility type to check if array types match between Result and NewResult.
* Returns either the valid NewResult type or an error message type.
Expand All @@ -117,14 +113,21 @@ export type CheckMatchingArrayTypes<Result, NewResult> =
: IsValidResultOverride<
Result,
NewResult,
NewResult,
{
Error: 'Type mismatch: Cannot cast array result to a single object. Use .returns<Array<YourType>> for array results or .single() to convert the result to a single object'
},
{
Error: 'Type mismatch: Cannot cast single object to array type. Remove Array wrapper from return type or make sure you are not using .single() up in the calling chain'
}
>
> extends infer ValidationResult
? ValidationResult extends true
? // Preserve the optionality of the result if the overriden type is an object (case of chaining with `maybeSingle`)
ContainsNull<Result> extends true
? NewResult | null
: NewResult
: // contains the error
ValidationResult
: never

type Simplify<T> = T extends object ? { [K in keyof T]: T[K] } : T

Expand Down
47 changes: 45 additions & 2 deletions test/override-types.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const postgrest = new PostgrestClient<Database>(REST_URL)
let result: typeof singleResult.data
expectType<TypeEqual<(typeof result)['custom_field'], string>>(true)
}
// Test with maybeSingle()
// Test with maybeSingle() merging with new field
{
const maybeSingleResult = await postgrest
.from('users')
Expand All @@ -71,7 +71,50 @@ const postgrest = new PostgrestClient<Database>(REST_URL)
throw new Error(maybeSingleResult.error.message)
}
let maybeSingleResultType: typeof maybeSingleResult.data
let expectedType: { custom_field: string } | null
let expectedType: {
age_range: unknown
catchphrase: unknown
data: CustomUserDataType | null
status: 'ONLINE' | 'OFFLINE' | null
username: string
custom_field: string
} | null
expectType<TypeEqual<typeof maybeSingleResultType, typeof expectedType>>(true)
}
// Test with maybeSingle() merging with override field
{
const maybeSingleResult = await postgrest
.from('users')
.select()
.maybeSingle()
.overrideTypes<{ catchphrase: string }>()
if (maybeSingleResult.error) {
throw new Error(maybeSingleResult.error.message)
}
let maybeSingleResultType: typeof maybeSingleResult.data
let expectedType: {
age_range: unknown
catchphrase: string
data: CustomUserDataType | null
status: 'ONLINE' | 'OFFLINE' | null
username: string
} | null
expectType<TypeEqual<typeof maybeSingleResultType, typeof expectedType>>(true)
}
// Test with maybeSingle() replace with override field
{
const maybeSingleResult = await postgrest
.from('users')
.select()
.maybeSingle()
.overrideTypes<{ catchphrase: string }, { merge: false }>()
if (maybeSingleResult.error) {
throw new Error(maybeSingleResult.error.message)
}
let maybeSingleResultType: typeof maybeSingleResult.data
let expectedType: {
catchphrase: string
} | null
expectType<TypeEqual<typeof maybeSingleResultType, typeof expectedType>>(true)
}
// Test replacing behavior
Expand Down

0 comments on commit 578c69d

Please sign in to comment.