Skip to content

Commit 578c69d

Browse files
committed
fix(types): overrideTypes merging with maybeSingle
1 parent c8754b7 commit 578c69d

File tree

3 files changed

+68
-15
lines changed

3 files changed

+68
-15
lines changed

src/PostgrestBuilder.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
IsValidResultOverride,
1111
} from './types'
1212
import PostgrestError from './PostgrestError'
13+
import { ContainsNull } from './select-query-parser/types'
1314

1415
export default abstract class PostgrestBuilder<Result, ThrowOnError extends boolean = false>
1516
implements
@@ -257,14 +258,20 @@ export default abstract class PostgrestBuilder<Result, ThrowOnError extends bool
257258
NewResult,
258259
Options extends { merge?: boolean } = { merge: true }
259260
>(): PostgrestBuilder<
260-
IsValidResultOverride<Result, NewResult, true, false, false> extends true
261-
? MergePartialResult<NewResult, Result, Options>
261+
IsValidResultOverride<Result, NewResult, false, false> extends true
262+
? // Preserve the optionality of the result if the overriden type is an object (case of chaining with `maybeSingle`)
263+
ContainsNull<Result> extends true
264+
? MergePartialResult<NewResult, NonNullable<Result>, Options> | null
265+
: MergePartialResult<NewResult, Result, Options>
262266
: CheckMatchingArrayTypes<Result, NewResult>,
263267
ThrowOnError
264268
> {
265269
return this as unknown as PostgrestBuilder<
266-
IsValidResultOverride<Result, NewResult, true, false, false> extends true
267-
? MergePartialResult<NewResult, Result, Options>
270+
IsValidResultOverride<Result, NewResult, false, false> extends true
271+
? // Preserve the optionality of the result if the overriden type is an object (case of chaining with `maybeSingle`)
272+
ContainsNull<Result> extends true
273+
? MergePartialResult<NewResult, NonNullable<Result>, Options> | null
274+
: MergePartialResult<NewResult, Result, Options>
268275
: CheckMatchingArrayTypes<Result, NewResult>,
269276
ThrowOnError
270277
>

src/types.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -92,20 +92,16 @@ type NonRecursiveType = BuiltIns | Function | (new (...arguments_: any[]) => unk
9292
type BuiltIns = Primitive | void | Date | RegExp
9393
type Primitive = null | undefined | string | number | boolean | symbol | bigint
9494

95-
export type IsValidResultOverride<Result, NewResult, Ok, ErrorResult, ErrorNewResult> =
95+
export type IsValidResultOverride<Result, NewResult, ErrorResult, ErrorNewResult> =
9696
Result extends any[]
9797
? NewResult extends any[]
9898
? // Both are arrays - valid
99-
Ok
99+
true
100100
: ErrorResult
101101
: NewResult extends any[]
102102
? ErrorNewResult
103103
: // Neither are arrays - valid
104-
// Preserve the optionality of the result if the overriden type is an object (case of chaining with `maybeSingle`)
105-
ContainsNull<Result> extends true
106-
? Ok | null
107-
: Ok
108-
104+
true
109105
/**
110106
* Utility type to check if array types match between Result and NewResult.
111107
* Returns either the valid NewResult type or an error message type.
@@ -117,14 +113,21 @@ export type CheckMatchingArrayTypes<Result, NewResult> =
117113
: IsValidResultOverride<
118114
Result,
119115
NewResult,
120-
NewResult,
121116
{
122117
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'
123118
},
124119
{
125120
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'
126121
}
127-
>
122+
> extends infer ValidationResult
123+
? ValidationResult extends true
124+
? // Preserve the optionality of the result if the overriden type is an object (case of chaining with `maybeSingle`)
125+
ContainsNull<Result> extends true
126+
? NewResult | null
127+
: NewResult
128+
: // contains the error
129+
ValidationResult
130+
: never
128131

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

test/override-types.test-d.ts

+45-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const postgrest = new PostgrestClient<Database>(REST_URL)
6060
let result: typeof singleResult.data
6161
expectType<TypeEqual<(typeof result)['custom_field'], string>>(true)
6262
}
63-
// Test with maybeSingle()
63+
// Test with maybeSingle() merging with new field
6464
{
6565
const maybeSingleResult = await postgrest
6666
.from('users')
@@ -71,7 +71,50 @@ const postgrest = new PostgrestClient<Database>(REST_URL)
7171
throw new Error(maybeSingleResult.error.message)
7272
}
7373
let maybeSingleResultType: typeof maybeSingleResult.data
74-
let expectedType: { custom_field: string } | null
74+
let expectedType: {
75+
age_range: unknown
76+
catchphrase: unknown
77+
data: CustomUserDataType | null
78+
status: 'ONLINE' | 'OFFLINE' | null
79+
username: string
80+
custom_field: string
81+
} | null
82+
expectType<TypeEqual<typeof maybeSingleResultType, typeof expectedType>>(true)
83+
}
84+
// Test with maybeSingle() merging with override field
85+
{
86+
const maybeSingleResult = await postgrest
87+
.from('users')
88+
.select()
89+
.maybeSingle()
90+
.overrideTypes<{ catchphrase: string }>()
91+
if (maybeSingleResult.error) {
92+
throw new Error(maybeSingleResult.error.message)
93+
}
94+
let maybeSingleResultType: typeof maybeSingleResult.data
95+
let expectedType: {
96+
age_range: unknown
97+
catchphrase: string
98+
data: CustomUserDataType | null
99+
status: 'ONLINE' | 'OFFLINE' | null
100+
username: string
101+
} | null
102+
expectType<TypeEqual<typeof maybeSingleResultType, typeof expectedType>>(true)
103+
}
104+
// Test with maybeSingle() replace with override field
105+
{
106+
const maybeSingleResult = await postgrest
107+
.from('users')
108+
.select()
109+
.maybeSingle()
110+
.overrideTypes<{ catchphrase: string }, { merge: false }>()
111+
if (maybeSingleResult.error) {
112+
throw new Error(maybeSingleResult.error.message)
113+
}
114+
let maybeSingleResultType: typeof maybeSingleResult.data
115+
let expectedType: {
116+
catchphrase: string
117+
} | null
75118
expectType<TypeEqual<typeof maybeSingleResultType, typeof expectedType>>(true)
76119
}
77120
// Test replacing behavior

0 commit comments

Comments
 (0)