Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions packages/core/postgrest-js/src/PostgrestFilterBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ type ResolveFilterRelationshipValue<
: unknown
: never

export type ResolveContainsFilterValue<Resolved> =
NonNullable<Resolved> extends readonly (infer Element)[]
? readonly Element[]
: string | readonly unknown[] | Record<string, unknown>

export type InvalidMethodError<S extends string> = { Error: S }

type NonNullableColumn<T extends Record<string, unknown>, Col extends string> = Col extends keyof T
Expand Down Expand Up @@ -1004,12 +1009,12 @@ export default class PostgrestFilterBuilder<
* }
* ```
*/
contains<ColumnName extends string & keyof Row>(
contains<ColumnName extends string>(
column: ColumnName,
value: string | ReadonlyArray<Row[ColumnName]> | Record<string, unknown>
): this
contains(column: string, value: string | readonly unknown[] | Record<string, unknown>): this
contains(column: string, value: string | readonly unknown[] | Record<string, unknown>): this {
value: ColumnName extends keyof Row
? ResolveContainsFilterValue<Row[ColumnName]>
: string | readonly unknown[] | Record<string, unknown>
): this {
if (typeof value === 'string') {
// range types can be inclusive '[', ']' or exclusive '(', ')' so just
// keep it simple and accept a string
Expand Down Expand Up @@ -1154,12 +1159,12 @@ export default class PostgrestFilterBuilder<
*
* ```
*/
containedBy<ColumnName extends string & keyof Row>(
containedBy<ColumnName extends string>(
column: ColumnName,
value: string | ReadonlyArray<Row[ColumnName]> | Record<string, unknown>
): this
containedBy(column: string, value: string | readonly unknown[] | Record<string, unknown>): this
containedBy(column: string, value: string | readonly unknown[] | Record<string, unknown>): this {
value: ColumnName extends keyof Row
? ResolveContainsFilterValue<Row[ColumnName]>
: string | readonly unknown[] | Record<string, unknown>
): this {
if (typeof value === 'string') {
// range
this.url.searchParams.append(column, `cd.${value}`)
Expand Down
34 changes: 34 additions & 0 deletions packages/core/postgrest-js/test/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,3 +563,37 @@ type WithThrowOnError<T> =
expectType<TypeEqual<typeof result.data, { id: number; message: string }[]>>(true)
}
}

// .contains() and .containedBy() value typing (Issue #435)
{
// Array columns require an array of the element type
postgrest.from('cornercase').select().contains('array_column', ['test'])
postgrest
.from('cornercase')
.select()
.contains('array_column', ['test'] as const)
postgrest.from('cornercase').select().containedBy('array_column', ['test'])
postgrest
.from('cornercase')
.select()
.containedBy('array_column', ['test'] as const)

// @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'readonly string[]'
postgrest.from('cornercase').select().contains('array_column', 'test')
// @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'readonly string[]'
postgrest.from('cornercase').select().containedBy('array_column', 'test')
// @ts-expect-error Type 'number' is not assignable to type 'string'
postgrest.from('cornercase').select().contains('array_column', [123])
// @ts-expect-error Type 'number' is not assignable to type 'string'
postgrest.from('cornercase').select().containedBy('array_column', [123])
// @ts-expect-error Object literal may only specify known properties, and 'foo' does not exist in type 'readonly string[]'
postgrest.from('cornercase').select().contains('array_column', { foo: 'bar' })
// @ts-expect-error Object literal may only specify known properties, and 'foo' does not exist in type 'readonly string[]'
postgrest.from('cornercase').select().containedBy('array_column', { foo: 'bar' })

// Range and JSON columns accept string and objects
postgrest.from('users').select().contains('age_range', '[1,2)')
postgrest.from('users').select().containedBy('age_range', '[1,2)')
postgrest.from('users').select().contains('data', { foo: 'bar' })
postgrest.from('users').select().containedBy('data', { foo: 'bar' })
}