Not null
Sometimes you can be sure something's not null, but Kysely isn't able to infer
it. For example calling where('last_name', 'is not', null)
doesn't make
last_name
not null in the result type, but unless you have other where statements
you can be sure it's never null.
Kysely has a couple of helpers for dealing with these cases: $notNull()
and $narrowType
.
Both are used in the following example:
import type {
ColumnType,
Generated,
GeneratedAlways,
Insertable,
Kysely,
Selectable,
SqlBool,
Updateable,
} from 'kysely'
export interface Database {
audit: AuditTable
person: PersonTable
person_backup: PersonTable
pet: PetTable
toy: ToyTable
wine: WineTable
wine_stock_change: WineStockChangeTable
}
interface AuditTable {
id: Generated<number>
action: string
}
interface PersonTable {
id: Generated<number>
address: { city: string } | null
age: number | null
birthdate: ColumnType<Date | null, string | null | undefined, string | null>
created_at: GeneratedAlways<Date>
deleted_at: ColumnType<Date | null, string | null | undefined, string | null>
experience: { role: string }[] | null
first_name: string
gender: 'male' | 'female' | 'other' | null
has_pets: Generated<'Y' | 'N'>
last_name: string | null
middle_name: string | null
nicknames: string[] | null
nullable_column: string | null
profile: {
addresses: { city: string }[]
website: { url: string }
} | null
updated_at: ColumnType<Date | null, string | null | undefined, string | null>
marital_status: 'single' | 'married' | 'divorced' | 'widowed' | null
}
interface PetTable {
id: Generated<number>
created_at: GeneratedAlways<Date>
is_favorite: Generated<SqlBool>
name: string
owner_id: number
species: Species
}
interface ToyTable {
id: Generated<number>
name: string
pet_id: number
price: number
}
interface WineTable {
id: Generated<number>
name: string
stock: number
}
interface WineStockChangeTable {
id: Generated<number>
stock_delta: number
wine_name: string
}
export type Person = Selectable<PersonTable>
export type NewPerson = Insertable<PersonTable>
export type PersonUpdate = Updateable<PersonTable>
export type Pet = Selectable<PetTable>
export type NewPet = Insertable<PetTable>
export type PetUpdate = Updateable<PetTable>
export type Species = 'dog' | 'cat' | 'hamster'
declare global {
// @ts-ignore
export class Buffer {
static isBuffer(obj: unknown): obj is { length: number }
static compare(a: Buffer, b: Buffer): number
}
export const db: Kysely<Database>
export function functionThatExpectsPersonWithNonNullValue(
person: Person & { nullable_column: string },
): void
}
import { NotNull } from 'kysely'
import { jsonObjectFrom } from 'kysely/helpers/postgres'
const persons = db
.selectFrom('person')
.select((eb) => [
'last_name',
// Let's assume we know the person has at least one
// pet. We can use the `.$notNull()` method to make
// the expression not null. You could just as well
// add `pet` to the `$narrowType` call below.
jsonObjectFrom(
eb.selectFrom('pet')
.selectAll()
.limit(1)
.whereRef('person.id', '=', 'pet.owner_id')
).$notNull().as('pet')
])
.where('last_name', 'is not', null)
// $narrowType can be used to narrow the output type.
// The special `NotNull` type can be used to make a
// selection not null. You could add `pet: NotNull`
// here and omit the `$notNull()` call on it.
// Use whichever way you prefer.
.$narrowType<{ last_name: NotNull }>()
.execute()
More examples
The API documentation is packed with examples. The API docs are hosted here, but you can access the same documentation by hovering over functions/methods/classes in your IDE. The examples are always just one hover away!
For example, check out these sections: