Skip to content

Commit

Permalink
Make IDEs show simple result types
Browse files Browse the repository at this point in the history
  • Loading branch information
koskimas committed Mar 6, 2023
1 parent 278d31f commit a8e28d9
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 46 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "kysely",
"version": "0.23.4",
"version": "0.23.5",
"description": "Type safe SQL query builder",
"repository": {
"type": "git",
Expand Down
21 changes: 13 additions & 8 deletions src/query-builder/delete-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ import {
import { ReturningRow } from '../parser/returning-parser.js'
import { ReferenceExpression } from '../parser/reference-parser.js'
import { QueryNode } from '../operation-node/query-node.js'
import { MergePartial, Nullable, SingleResultType } from '../util/type-utils.js'
import {
MergePartial,
Nullable,
SimplifyResult,
SimplifySingleResult,
} from '../util/type-utils.js'
import { preventAwait } from '../util/prevent-await.js'
import { Compilable } from '../util/compilable.js'
import { QueryExecutor } from '../query-executor/query-executor.js'
Expand Down Expand Up @@ -779,7 +784,7 @@ export class DeleteQueryBuilder<DB, TB extends keyof DB, O>
)
}

compile(): CompiledQuery<O> {
compile(): CompiledQuery<SimplifyResult<O>> {
return this.#props.executor.compileQuery(
this.toOperationNode(),
this.#props.queryId
Expand All @@ -791,7 +796,7 @@ export class DeleteQueryBuilder<DB, TB extends keyof DB, O>
*
* Also see the {@link executeTakeFirst} and {@link executeTakeFirstOrThrow} methods.
*/
async execute(): Promise<O[]> {
async execute(): Promise<SimplifyResult<O>[]> {
const compiledQuery = this.compile()
const query = compiledQuery.query as DeleteQueryNode

Expand All @@ -801,7 +806,7 @@ export class DeleteQueryBuilder<DB, TB extends keyof DB, O>
)

if (this.#props.executor.adapter.supportsReturning && query.returning) {
return result.rows
return result.rows as any
}

return [
Expand All @@ -816,9 +821,9 @@ export class DeleteQueryBuilder<DB, TB extends keyof DB, O>
* Executes the query and returns the first result or undefined if
* the query returned no result.
*/
async executeTakeFirst(): Promise<SingleResultType<O>> {
async executeTakeFirst(): Promise<SimplifySingleResult<O>> {
const [result] = await this.execute()
return result as SingleResultType<O>
return result as SimplifySingleResult<O>
}

/**
Expand All @@ -831,14 +836,14 @@ export class DeleteQueryBuilder<DB, TB extends keyof DB, O>
*/
async executeTakeFirstOrThrow(
errorConstructor: NoResultErrorConstructor = NoResultError
): Promise<O> {
): Promise<SimplifyResult<O>> {
const result = await this.executeTakeFirst()

if (result === undefined) {
throw new errorConstructor(this.toOperationNode())
}

return result as O
return result as SimplifyResult<O>
}

/**
Expand Down
18 changes: 11 additions & 7 deletions src/query-builder/insert-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import {
} from '../parser/insert-values-parser.js'
import { InsertQueryNode } from '../operation-node/insert-query-node.js'
import { QueryNode } from '../operation-node/query-node.js'
import { MergePartial, SingleResultType } from '../util/type-utils.js'
import {
MergePartial,
SimplifyResult,
SimplifySingleResult,
} from '../util/type-utils.js'
import { UpdateObject, parseUpdateObject } from '../parser/update-set-parser.js'
import { preventAwait } from '../util/prevent-await.js'
import { Compilable } from '../util/compilable.js'
Expand Down Expand Up @@ -726,7 +730,7 @@ export class InsertQueryBuilder<DB, TB extends keyof DB, O>
*
* Also see the {@link executeTakeFirst} and {@link executeTakeFirstOrThrow} methods.
*/
async execute(): Promise<O[]> {
async execute(): Promise<SimplifyResult<O>[]> {
const compiledQuery = this.compile()
const query = compiledQuery.query as InsertQueryNode

Expand All @@ -736,7 +740,7 @@ export class InsertQueryBuilder<DB, TB extends keyof DB, O>
)

if (this.#props.executor.adapter.supportsReturning && query.returning) {
return result.rows
return result.rows as any
}

return [
Expand All @@ -752,9 +756,9 @@ export class InsertQueryBuilder<DB, TB extends keyof DB, O>
* Executes the query and returns the first result or undefined if
* the query returned no result.
*/
async executeTakeFirst(): Promise<SingleResultType<O>> {
async executeTakeFirst(): Promise<SimplifySingleResult<O>> {
const [result] = await this.execute()
return result as SingleResultType<O>
return result as SimplifySingleResult<O>
}

/**
Expand All @@ -767,14 +771,14 @@ export class InsertQueryBuilder<DB, TB extends keyof DB, O>
*/
async executeTakeFirstOrThrow(
errorConstructor: NoResultErrorConstructor = NoResultError
): Promise<O> {
): Promise<SimplifyResult<O>> {
const result = await this.executeTakeFirst()

if (result === undefined) {
throw new errorConstructor(this.toOperationNode())
}

return result as O
return result as SimplifyResult<O>
}

/**
Expand Down
17 changes: 11 additions & 6 deletions src/query-builder/select-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ import {
} from '../parser/reference-parser.js'
import { SelectQueryNode } from '../operation-node/select-query-node.js'
import { QueryNode } from '../operation-node/query-node.js'
import { MergePartial, Nullable, SingleResultType } from '../util/type-utils.js'
import {
MergePartial,
Nullable,
Simplify,
SimplifySingleResult,
} from '../util/type-utils.js'
import {
OrderByDirectionExpression,
OrderByExpression,
Expand Down Expand Up @@ -1791,7 +1796,7 @@ export class SelectQueryBuilder<DB, TB extends keyof DB, O>
)
}

compile(): CompiledQuery<O> {
compile(): CompiledQuery<Simplify<O>> {
return this.#props.executor.compileQuery(
this.toOperationNode(),
this.#props.queryId
Expand All @@ -1803,7 +1808,7 @@ export class SelectQueryBuilder<DB, TB extends keyof DB, O>
*
* Also see the {@link executeTakeFirst} and {@link executeTakeFirstOrThrow} methods.
*/
async execute(): Promise<O[]> {
async execute(): Promise<Simplify<O>[]> {
const compiledQuery = this.compile()

const result = await this.#props.executor.executeQuery<O>(
Expand All @@ -1818,9 +1823,9 @@ export class SelectQueryBuilder<DB, TB extends keyof DB, O>
* Executes the query and returns the first result or undefined if
* the query returned no result.
*/
async executeTakeFirst(): Promise<SingleResultType<O>> {
async executeTakeFirst(): Promise<SimplifySingleResult<O>> {
const [result] = await this.execute()
return result as SingleResultType<O>
return result as SimplifySingleResult<O>
}

/**
Expand All @@ -1833,7 +1838,7 @@ export class SelectQueryBuilder<DB, TB extends keyof DB, O>
*/
async executeTakeFirstOrThrow(
errorConstructor: NoResultErrorConstructor = NoResultError
): Promise<O> {
): Promise<Simplify<O>> {
const result = await this.executeTakeFirst()

if (result === undefined) {
Expand Down
21 changes: 13 additions & 8 deletions src/query-builder/update-query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ import {
import { ReturningRow } from '../parser/returning-parser.js'
import { ReferenceExpression } from '../parser/reference-parser.js'
import { QueryNode } from '../operation-node/query-node.js'
import { MergePartial, Nullable, SingleResultType } from '../util/type-utils.js'
import {
MergePartial,
Nullable,
SimplifyResult,
SimplifySingleResult,
} from '../util/type-utils.js'
import { UpdateQueryNode } from '../operation-node/update-query-node.js'
import { UpdateObject, parseUpdateObject } from '../parser/update-set-parser.js'
import { preventAwait } from '../util/prevent-await.js'
Expand Down Expand Up @@ -777,7 +782,7 @@ export class UpdateQueryBuilder<DB, UT extends keyof DB, TB extends keyof DB, O>
)
}

compile(): CompiledQuery<O> {
compile(): CompiledQuery<SimplifyResult<O>> {
return this.#props.executor.compileQuery(
this.toOperationNode(),
this.#props.queryId
Expand All @@ -789,7 +794,7 @@ export class UpdateQueryBuilder<DB, UT extends keyof DB, TB extends keyof DB, O>
*
* Also see the {@link executeTakeFirst} and {@link executeTakeFirstOrThrow} methods.
*/
async execute(): Promise<O[]> {
async execute(): Promise<SimplifyResult<O>[]> {
const compiledQuery = this.compile()
const query = compiledQuery.query as UpdateQueryNode

Expand All @@ -799,7 +804,7 @@ export class UpdateQueryBuilder<DB, UT extends keyof DB, TB extends keyof DB, O>
)

if (this.#props.executor.adapter.supportsReturning && query.returning) {
return result.rows
return result.rows as any
}

return [
Expand All @@ -814,9 +819,9 @@ export class UpdateQueryBuilder<DB, UT extends keyof DB, TB extends keyof DB, O>
* Executes the query and returns the first result or undefined if
* the query returned no result.
*/
async executeTakeFirst(): Promise<SingleResultType<O>> {
async executeTakeFirst(): Promise<SimplifySingleResult<O>> {
const [result] = await this.execute()
return result as SingleResultType<O>
return result as SimplifySingleResult<O>
}

/**
Expand All @@ -829,14 +834,14 @@ export class UpdateQueryBuilder<DB, UT extends keyof DB, TB extends keyof DB, O>
*/
async executeTakeFirstOrThrow(
errorConstructor: NoResultErrorConstructor = NoResultError
): Promise<O> {
): Promise<SimplifyResult<O>> {
const result = await this.executeTakeFirst()

if (result === undefined) {
throw new errorConstructor(this.toOperationNode())
}

return result as O
return result as SimplifyResult<O>
}

/**
Expand Down
17 changes: 12 additions & 5 deletions src/util/type-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,23 @@ export type ArrayItemType<T> = T extends ReadonlyArray<infer I> ? I : never
*/
export type AnySelectQueryBuilder = SelectQueryBuilder<any, any, any>

/**
* Given a query output type `O` evaluates to the output type of a `executeTakeFirst` call.
*/
export type SingleResultType<O> = O extends InsertResult
export type SimplifySingleResult<O> = O extends InsertResult
? O
: O extends DeleteResult
? O
: O extends UpdateResult
? O
: O | undefined
: Simplify<O> | undefined

export type SimplifyResult<O> = O extends InsertResult
? O
: O extends DeleteResult
? O
: O extends UpdateResult
? O
: Simplify<O>

export type Simplify<T> = { [K in keyof T]: T[K] } & {}

/**
* Represents a database row whose column names and their types are unknown.
Expand Down
18 changes: 9 additions & 9 deletions test/typings/test-d/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ async function testSelectSingle(db: Kysely<Database>) {
.select(sql`random() > 0.5`.as(alias))
.select('first_name')
.execute()
expectType<{ first_name: string } & { [key: string]: unknown }>(r5)
expectType<{ first_name: string; [key: string]: unknown }>(r5)

// Subquery
const [r6] = await qb
Expand Down Expand Up @@ -677,7 +677,7 @@ async function testCall(db: Kysely<Database>) {
.call((qb) => qb.select('name'))
.execute()

expectType<{ species: 'dog' | 'cat' } & { name: string }>(r1)
expectType<{ species: 'dog' | 'cat'; name: string }>(r1)
}

async function testIf(db: Kysely<Database>) {
Expand All @@ -690,7 +690,7 @@ async function testIf(db: Kysely<Database>) {
.if(condition, (qb) => qb.select('name'))
.execute()

expectType<{ species: 'dog' | 'cat' } & { name?: string }>(r1)
expectType<{ species: 'dog' | 'cat'; name?: string }>(r1)

// Conditional returning in delete
const [r2] = await db
Expand All @@ -707,7 +707,7 @@ async function testIf(db: Kysely<Database>) {
.if(condition, (qb) => qb.returning('last_name'))
.execute()

expectType<{ first_name: string } & { last_name?: string | null }>(r3)
expectType<{ first_name: string; last_name?: string | null }>(r3)

// Conditional where in delete
const [r4] = await db
Expand All @@ -724,7 +724,7 @@ async function testIf(db: Kysely<Database>) {
.if(condition, (qb) => qb.where('id', '=', 1))
.execute()

expectType<{ first_name: string } & Partial<{}>>(r5)
expectType<{ first_name: string }>(r5)

// Conditional returning in update
const [r6] = await db
Expand All @@ -743,7 +743,7 @@ async function testIf(db: Kysely<Database>) {
.if(condition, (qb) => qb.returning('last_name'))
.execute()

expectType<{ first_name: string } & { last_name?: string | null }>(r7)
expectType<{ first_name: string; last_name?: string | null }>(r7)

// Conditional where in update
const [r8] = await db
Expand All @@ -762,7 +762,7 @@ async function testIf(db: Kysely<Database>) {
.if(condition, (qb) => qb.where('id', '=', 1))
.execute()

expectType<{ first_name: string } & Partial<{}>>(r9)
expectType<{ first_name: string }>(r9)

// Conditional returning in insert
const [r10] = await db
Expand All @@ -781,7 +781,7 @@ async function testIf(db: Kysely<Database>) {
.if(condition, (qb) => qb.returning('last_name'))
.execute()

expectType<{ first_name: string } & { last_name?: string | null }>(r11)
expectType<{ first_name: string; last_name?: string | null }>(r11)

// Conditional ingore in insert
const [r12] = await db
Expand All @@ -800,7 +800,7 @@ async function testIf(db: Kysely<Database>) {
.if(condition, (qb) => qb.ignore())
.execute()

expectType<{ first_name: string } & Partial<{}>>(r13)
expectType<{ first_name: string }>(r13)
}

async function testGenericSelect<T extends keyof Database>(
Expand Down

0 comments on commit a8e28d9

Please sign in to comment.