Skip to content

Commit

Permalink
Add
Browse files Browse the repository at this point in the history
  • Loading branch information
koskimas committed Dec 30, 2023
1 parent c011a29 commit ad194e1
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 19 deletions.
19 changes: 16 additions & 3 deletions src/expression/expression-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,20 @@ export class ExpressionWrapper<DB, TB extends keyof DB, T>
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of this `ExpressionWrapper` with a new output type.
*/
$castTo<T>(): ExpressionWrapper<DB, TB, T> {
$castTo<C>(): ExpressionWrapper<DB, TB, C> {
return new ExpressionWrapper(this.#node)
}

/**
* Omit null from the expression's type.
*
* This function can be useful in cases where you know an expression can't be
* null, but Kysely is unable to infer it.
*
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of `this` with a new output type.
*/
$notNull(): ExpressionWrapper<DB, TB, Exclude<T, null>> {
return new ExpressionWrapper(this.#node)
}

Expand Down Expand Up @@ -352,7 +365,7 @@ export class OrWrapper<DB, TB extends keyof DB, T extends SqlBool>
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of this `OrWrapper` with a new output type.
*/
$castTo<T extends SqlBool>(): OrWrapper<DB, TB, T> {
$castTo<C extends SqlBool>(): OrWrapper<DB, TB, C> {
return new OrWrapper(this.#node)
}

Expand Down Expand Up @@ -436,7 +449,7 @@ export class AndWrapper<DB, TB extends keyof DB, T extends SqlBool>
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of this `AndWrapper` with a new output type.
*/
$castTo<T extends SqlBool>(): AndWrapper<DB, TB, T> {
$castTo<C extends SqlBool>(): AndWrapper<DB, TB, C> {
return new AndWrapper(this.#node)
}

Expand Down
23 changes: 23 additions & 0 deletions src/query-builder/aggregate-function-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,29 @@ export class AggregateFunctionBuilder<DB, TB extends keyof DB, O = unknown>
return func(this)
}

/**
* Casts the expression to the given type.
*
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of this `AggregateFunctionBuilder` with a new output type.
*/
$castTo<C>(): AggregateFunctionBuilder<DB, TB, C> {
return new AggregateFunctionBuilder(this.#props)
}

/**
* Omit null from the expression's type.
*
* This function can be useful in cases where you know an expression can't be
* null, but Kysely is unable to infer it.
*
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of `this` with a new output type.
*/
$notNull(): AggregateFunctionBuilder<DB, TB, Exclude<O, null>> {
return new AggregateFunctionBuilder(this.#props)
}

toOperationNode(): AggregateFunctionNode {
return this.#props.aggregateFunctionNode
}
Expand Down
6 changes: 3 additions & 3 deletions src/query-builder/delete-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -716,10 +716,10 @@ export class DeleteQueryBuilder<DB, TB extends keyof DB, O>
/**
* Change the output type of the query.
*
* You should only use this method as the last resort if the types
* don't support your use case.
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of this `DeleteQueryBuilder` with a new output type.
*/
$castTo<T>(): DeleteQueryBuilder<DB, TB, T> {
$castTo<C>(): DeleteQueryBuilder<DB, TB, C> {
return new DeleteQueryBuilder(this.#props)
}

Expand Down
6 changes: 3 additions & 3 deletions src/query-builder/insert-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -677,10 +677,10 @@ export class InsertQueryBuilder<DB, TB extends keyof DB, O>
/**
* Change the output type of the query.
*
* You should only use this method as the last resort if the types
* don't support your use case.
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of this `InsertQueryBuilder` with a new output type.
*/
$castTo<T>(): InsertQueryBuilder<DB, TB, T> {
$castTo<C>(): InsertQueryBuilder<DB, TB, C> {
return new InsertQueryBuilder(this.#props)
}

Expand Down
6 changes: 5 additions & 1 deletion src/query-builder/json-path-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,11 @@ export class TraversedJSONPathBuilder<S, O>
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of this `JSONPathBuilder` with a new output type.
*/
$castTo<T>(): JSONPathBuilder<T> {
$castTo<C>(): JSONPathBuilder<C> {
return new JSONPathBuilder(this.#node)
}

$notNull(): JSONPathBuilder<Exclude<O, null>> {
return new JSONPathBuilder(this.#node)
}

Expand Down
8 changes: 4 additions & 4 deletions src/query-builder/select-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1439,10 +1439,10 @@ export interface SelectQueryBuilder<DB, TB extends keyof DB, O>
/**
* Change the output type of the query.
*
* You should only use this method as the last resort if the types
* don't support your use case.
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of this `SelectQueryBuilder` with a new output type.
*/
$castTo<T>(): SelectQueryBuilder<DB, TB, T>
$castTo<C>(): SelectQueryBuilder<DB, TB, C>

/**
* Changes the output type from an object to a tuple.
Expand Down Expand Up @@ -2095,7 +2095,7 @@ class SelectQueryBuilderImpl<DB, TB extends keyof DB, O>
}) as any
}

$castTo<T>(): SelectQueryBuilder<DB, TB, T> {
$castTo<C>(): SelectQueryBuilder<DB, TB, C> {
return new SelectQueryBuilderImpl(this.#props)
}

Expand Down
6 changes: 3 additions & 3 deletions src/query-builder/update-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,10 +681,10 @@ export class UpdateQueryBuilder<DB, UT extends keyof DB, TB extends keyof DB, O>
/**
* Change the output type of the query.
*
* You should only use this method as the last resort if the types
* don't support your use case.
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of this `UpdateQueryBuilder` with a new output type.
*/
$castTo<T>(): UpdateQueryBuilder<DB, UT, TB, T> {
$castTo<C>(): UpdateQueryBuilder<DB, UT, TB, C> {
return new UpdateQueryBuilder(this.#props)
}

Expand Down
19 changes: 17 additions & 2 deletions src/raw-builder/raw-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,18 @@ export interface RawBuilder<O> extends AliasableExpression<O> {
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of this `RawBuilder` with a new output type.
*/
$castTo<T>(): RawBuilder<T>
$castTo<C>(): RawBuilder<C>

/**
* Omit null from the expression's type.
*
* This function can be useful in cases where you know an expression can't be
* null, but Kysely is unable to infer it.
*
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of `this` with a new output type.
*/
$notNull(): RawBuilder<Exclude<O, null>>

/**
* Adds a plugin for this SQL snippet.
Expand Down Expand Up @@ -140,10 +151,14 @@ class RawBuilderImpl<O> implements RawBuilder<O> {
return new AliasedRawBuilderImpl(this, alias)
}

$castTo<T>(): RawBuilder<T> {
$castTo<C>(): RawBuilder<C> {
return new RawBuilderImpl({ ...this.#props })
}

$notNull(): RawBuilder<Exclude<O, null>> {
return new RawBuilderImpl(this.#props)
}

withPlugin(plugin: KyselyPlugin): RawBuilder<O> {
return new RawBuilderImpl({
...this.#props,
Expand Down
12 changes: 12 additions & 0 deletions test/typings/test-d/expression.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ async function testExpressionBuilder(
})
)

expectAssignable<Expression<number | null>>(
eb.case().when('age', '=', 10).then(1).else(null).end()
)

expectNotAssignable<Expression<number>>(
eb.case().when('age', '=', 10).then(1).else(null).end()
)

expectAssignable<Expression<number>>(
eb.case().when('age', '=', 10).then(1).else(null).end().$notNull()
)

expectType<
KyselyTypeError<'or() method can only be called on boolean expressions'>
>(eb('age', '+', 1).or('age', '=', 1))
Expand Down
41 changes: 41 additions & 0 deletions test/typings/test-d/postgres-json.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,47 @@ async function testPostgresJsonAgg(db: Kysely<Database>) {
pets: Selectable<Pet>[] | null
}[]
>(r3)

const db2 = db.withTables<{
acquisition: {
id: number
}
transaction: {
id: number
acquisitionId: number
status: string
}
}>()

const r4 = await db2
.selectFrom('acquisition')
.leftJoin('transaction', 'transaction.acquisitionId', 'acquisition.id')
.select(({ ref, fn }) => [
'acquisition.id',
fn
.coalesce(
fn
.jsonAgg(
jsonBuildObject({
id: ref('transaction.id').$notNull(),
status: ref('transaction.status'),
})
)
.filterWhere('transaction.id', 'is not', null),
sql`'[]'`
)
.as('transactions'),
])
.groupBy('acquisition.id')
.executeTakeFirstOrThrow()

expectType<{
id: number
transactions: {
id: number
status: string | null
}[]
}>(r4)
}

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

0 comments on commit ad194e1

Please sign in to comment.