Skip to content

Commit

Permalink
Add NotNull type constant for
Browse files Browse the repository at this point in the history
  • Loading branch information
koskimas committed Dec 29, 2023
1 parent 3120c65 commit ad3ee2b
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
14 changes: 14 additions & 0 deletions src/query-builder/select-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,20 @@ export interface SelectQueryBuilder<DB, TB extends keyof DB, O>
*
* functionThatExpectsPersonWithNonNullValue(person)
* ```
*
* Giving the explicit narrowed type (`string` in the example above) works fine for
* simple types. If the type is complex, for example a JSON column or a subquery,
* you can use the special `NotNull` type to make the column not null.
*
* ```ts
* const person = await db.selectFrom('person')
* .where('nullable_column', 'is not', null)
* .selectAll()
* .$narrowType<{ nullable_column: NotNull }>()
* .executeTakeFirstOrThrow()
*
* functionThatExpectsPersonWithNonNullValue(person)
* ```
*/
$narrowType<T>(): SelectQueryBuilder<DB, TB, NarrowPartial<O, T>>

Expand Down
25 changes: 21 additions & 4 deletions src/util/type-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,18 +178,35 @@ export type Equals<T, U> = (<G>() => G extends T ? 1 : 2) extends <
? true
: false

export type NarrowPartial<S, T> = DrainOuterGeneric<
export type NarrowPartial<O, T> = DrainOuterGeneric<
T extends object
? {
[K in keyof S & string]: K extends keyof T
? T[K] extends S[K]
[K in keyof O & string]: K extends keyof T
? T[K] extends NotNull
? Exclude<O[K], null>
: T[K] extends O[K]
? T[K]
: KyselyTypeError<`$narrowType() call failed: passed type does not exist in '${K}'s type union`>
: S[K]
: O[K]
}
: never
>

/**
* A type constant for marking a column as not null. Can be used with `$narrowPartial`.
*
* Example:
*
* ```ts
* const person = await db.selectFrom('person')
* .where('nullable_column', 'is not', null)
* .selectAll()
* .$narrowType<{ nullable_column: NotNull }>()
* .executeTakeFirstOrThrow()
* ```
*/
export type NotNull = { readonly __excludeNull__: true }

export type SqlBool = boolean | 0 | 1

/**
Expand Down
29 changes: 27 additions & 2 deletions test/typings/test-d/select.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Expression, Kysely, RawBuilder, Selectable, Simplify, sql } from '..'
import {
Expression,
Kysely,
NotNull,
RawBuilder,
Selectable,
Simplify,
sql,
} from '..'
import { Database, Person } from '../shared'
import { expectType, expectError } from 'tsd'

Expand Down Expand Up @@ -106,7 +114,24 @@ async function testSelectSingle(db: Kysely<Database>) {
.$narrowType<NarrowTarget>()
.execute()

expectType<NarrowTarget>(r15)
// Narrow not null
const [r16] = await db
.selectFrom('action')
.select(['callback_url', 'queue_id'])
.$narrowType<{ callback_url: NotNull }>()
.execute()

expectType<string>(r16.callback_url)
expectType<string | null>(r16.queue_id)

const [r17] = await db
.selectFrom('action')
.select(['callback_url', 'queue_id'])
.$narrowType<{ callback_url: NotNull; queue_id: NotNull }>()
.execute()

expectType<string>(r17.callback_url)
expectType<string>(r17.queue_id)
}

async function testSelectAll(db: Kysely<Database>) {
Expand Down

0 comments on commit ad3ee2b

Please sign in to comment.