UNPKG

kysely

Version:

Type safe SQL query builder

230 lines (229 loc) 6.97 kB
import type { DrainOuterGeneric, IsNever, IsNullable } from './type-utils.js'; /** * This type can be used to specify a different type for * select, insert and update operations. * * Also see the {@link Generated} type. * * ### Examples * * The next example defines a number column that is optional * in inserts and updates. All columns are always optional * in updates so therefore we don't need to specify `undefined` * for the update type. The type below is useful for all kinds of * database generated columns like identifiers. The `Generated` * type is actually just a shortcut for the type in this example: * * ```ts * type GeneratedNumber = ColumnType<number, number | undefined, number> * ``` * * The above example makes the column optional in inserts * and updates, but you can still choose to provide the * column. If you want to prevent insertion/update you * can se the type as `never`: * * ```ts * type ReadonlyNumber = ColumnType<number, never, never> * ``` * * Here's one more example where the type is different * for each different operation: * * ```ts * type UnupdateableDate = ColumnType<Date, string, never> * ``` */ export type ColumnType<SelectType, InsertType = SelectType, UpdateType = SelectType> = { readonly __select__: SelectType; readonly __insert__: InsertType; readonly __update__: UpdateType; }; /** * A shortcut for defining database-generated columns. The type * is the same for all selects, inserts and updates but the * column is optional for inserts and updates. * * The update type is `S` instead of `S | undefined` because updates are always * optional --> no need to specify optionality. * ``` */ export type Generated<S> = ColumnType<S, S | undefined, S>; /** * A shortcut for defining columns that are only database-generated * (like postgres GENERATED ALWAYS AS IDENTITY). No insert/update * is allowed. */ export type GeneratedAlways<S> = ColumnType<S, never, never>; /** * A shortcut for defining JSON columns, which are by default inserted/updated * as stringified JSON strings. */ export type JSONColumnType<SelectType extends object | null, InsertType = string, UpdateType = string> = ColumnType<SelectType, InsertType, UpdateType>; /** * Evaluates to `K` if `T` can be `null` or `undefined`. */ type IfNullable<T, K> = IsNullable<T> extends true ? K : never; /** * Evaluates to `K` if `T` can't be `null` or `undefined`. */ type IfNotNullable<T, K> = IsNullable<T> extends true ? never : IfNotNever<T, K>; /** * Evaluates to `K` if `T` isn't `never`. */ type IfNotNever<T, K> = IsNever<T> extends true ? never : K; export type SelectType<T> = T extends ColumnType<infer S, any, any> ? S : T; export type InsertType<T> = T extends ColumnType<any, infer I, any> ? I : T; export type UpdateType<T> = T extends ColumnType<any, any, infer U> ? U : T; /** * Keys of `R` whose `InsertType` values can be `null` or `undefined`. */ export type NullableInsertKeys<R> = { [K in keyof R]: IfNullable<InsertType<R[K]>, K>; }[keyof R]; /** * Keys of `R` whose `InsertType` values can't be `null` or `undefined`. */ export type NonNullableInsertKeys<R> = { [K in keyof R]: IfNotNullable<InsertType<R[K]>, K>; }[keyof R]; /** * Keys of `R` whose `SelectType` values are not `never` */ type NonNeverSelectKeys<R> = { [K in keyof R]: IfNotNever<SelectType<R[K]>, K>; }[keyof R]; /** * Keys of `R` whose `UpdateType` values are not `never` */ export type UpdateKeys<R> = { [K in keyof R]: IfNotNever<UpdateType<R[K]>, K>; }[keyof R]; /** * Given a table interface, extracts the select type from all * {@link ColumnType} types. * * ### Examples * * ```ts * interface PersonTable { * id: Generated<number> * first_name: string * modified_at: ColumnType<Date, string, never> * } * * type Person = Selectable<PersonTable> * // { * // id: number, * // first_name: string * // modified_at: Date * // } * ``` */ export type Selectable<R> = DrainOuterGeneric<{ [K in NonNeverSelectKeys<R>]: SelectType<R[K]>; }>; /** * Given a table interface, extracts the insert type from all * {@link ColumnType} types. * * ### Examples * * ```ts * interface PersonTable { * id: Generated<number> * first_name: string * modified_at: ColumnType<Date, string, never> * } * * type InsertablePerson = Insertable<PersonTable> * // { * // id?: number, * // first_name: string * // modified_at: string * // } * ``` */ export type Insertable<R> = DrainOuterGeneric<object & { [K in NonNullableInsertKeys<R>]: InsertType<R[K]>; } & { [K in NullableInsertKeys<R>]?: InsertType<R[K]>; }>; /** * Given a table interface, extracts the update type from all * {@link ColumnType} types. * * ### Examples * * ```ts * interface PersonTable { * id: Generated<number> * first_name: string * modified_at: ColumnType<Date, string, never> * } * * type UpdateablePerson = Updateable<PersonTable> * // { * // id?: number, * // first_name?: string * // } * ``` */ export type Updateable<R> = DrainOuterGeneric<{ [K in UpdateKeys<R>]?: UpdateType<R[K]> | undefined; }>; /** * Wrap your column type to opt-out from dehydration by {@link ShallowDehydrateValue}, * when used by JSON functions, such as the ones used by the relational helpers. * * Why need this? * * An edge case surfaced in issues where a numeric string column type was actually * not a numeric data type column, but text - and thus shouldn't be dehydrated * to `number`. * * Another use case would be, when someone finds a way to keep nested columns hydrated * at runtime, e.g. via a plugin, and wants to prevent dehydration of such columns * at the type level. * * ### Examples * * ```ts * import type { NonDehydrateable } from 'kysely' * import { jsonArrayFrom } from 'kysely/helpers/postgres' * * const result = await db * .withTables<{ * my_table: { * a_column: '1' | '2' | '3', * another_column: NonDehydrateable<'1' | '2' | '3'>, * column_too: NonDehydrateable<ColumnType<'1' | '2' | '3'>> * } * }>() * .selectFrom('person') * .select((eb) => [ * 'id', * jsonArrayFrom( * eb.selectFrom('my_table') * .select(['a_column', 'another_column', 'column_too']) * ).as('related') * ]) * .execute() * ``` * * In the example above, "a_column" will be dehydrated to `number` * given it is a numeric string - which is normally a numeric data type column * that gets output as a string by underlying database drivers to avoid * precision loss or overflow. * * "another_column" and "column_too" will remain as `'1' | '2' | '3'`, given they're * wrapped in `NonDehydrateable`, which prevents their dehydration. */ export type NonDehydrateable<T> = [T] extends [ ColumnType<infer S, infer I, infer U> ] ? ColumnType<S & { __kysely_dehydrate__?: false; }, I, U> : T & { __kysely_dehydrate__?: false; }; export {};