-
Notifications
You must be signed in to change notification settings - Fork 284
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement type predicates for query builders
- Loading branch information
1 parent
0387320
commit 074da5f
Showing
8 changed files
with
392 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import { DeleteQueryBuilder } from '../query-builder/delete-query-builder.js' | ||
import { InsertQueryBuilder } from '../query-builder/insert-query-builder.js' | ||
import { | ||
MatchedThenableMergeQueryBuilder, | ||
MergeQueryBuilder, | ||
NotMatchedThenableMergeQueryBuilder, | ||
WheneableMergeQueryBuilder, | ||
} from '../query-builder/merge-query-builder.js' | ||
import { SelectQueryBuilder } from '../query-builder/select-query-builder.js' | ||
import { UpdateQueryBuilder } from '../query-builder/update-query-builder.js' | ||
|
||
type AnyMergeQueryBuilder< | ||
DB, | ||
TT extends keyof DB, | ||
ST extends keyof DB, | ||
UT extends TT | ST, | ||
O | ||
> = | ||
| MergeQueryBuilder<DB, TT, O> | ||
| WheneableMergeQueryBuilder<DB, TT, ST, O> | ||
| MatchedThenableMergeQueryBuilder<DB, TT, ST, UT, O> | ||
| NotMatchedThenableMergeQueryBuilder<DB, TT, ST, O> | ||
|
||
/** | ||
* A helper type that can accept any query builder object. | ||
* | ||
* You can use helper methods to determine the type of query builder object. | ||
* | ||
* See {@link isSelectQueryBuilder}, {@link isInsertQueryBuilder}, {@link isUpdateQueryBuilder}, | ||
* {@link isDeleteQueryBuilder}, {@link isMergeQueryBuilder}. | ||
* | ||
* ### Example | ||
* ```ts | ||
* import { AnyQueryBuilder, isSelectQueryBuilder } from 'kysely' | ||
* | ||
* function alwaysSelectAll<DB, TB extends keyof DB, O>(qb: AnyQueryBuilder<DB, TB, O>) { | ||
* // Here `qb` could be any query builder object. | ||
* // We can use the helper method `isSelectQueryBuilder` to determine the type. | ||
* if (isSelectQueryBuilder(qb)) { | ||
* // We can now use `SelectQueryBuilder` methods and properties. | ||
* return qb.clearSelect().selectAll() | ||
* } | ||
* return qb | ||
* } | ||
* ``` | ||
*/ | ||
export type AnyQueryBuilder< | ||
DB, | ||
TB extends keyof DB, | ||
O, | ||
UT extends keyof DB = any, | ||
ST extends keyof DB = any | ||
> = | ||
| SelectQueryBuilder<DB, TB, O> | ||
| InsertQueryBuilder<DB, TB, O> | ||
| UpdateQueryBuilder<DB, UT, TB, O> | ||
| DeleteQueryBuilder<DB, TB, O> | ||
| AnyMergeQueryBuilder<DB, TB, ST, TB | ST, O> | ||
|
||
/** | ||
* A helper method to determine if a query builder object is of type {@link SelectQueryBuilder}. | ||
* | ||
* Useful when using the {@link AnyQueryBuilder} type. | ||
*/ | ||
export function isSelectQueryBuilder<DB, TB extends keyof DB, O>( | ||
qb: AnyQueryBuilder<DB, TB, O> | ||
): qb is SelectQueryBuilder<DB, TB, O> { | ||
return !!(qb as SelectQueryBuilder<DB, TB, O>).isSelectQueryBuilder | ||
} | ||
|
||
/** | ||
* A helper method to determine if a query builder object is of type {@link InsertQueryBuilder}. | ||
* | ||
* Useful when using the {@link AnyQueryBuilder} type. | ||
*/ | ||
export function isInsertQueryBuilder<DB, TB extends keyof DB, O>( | ||
qb: AnyQueryBuilder<DB, TB, O> | ||
): qb is InsertQueryBuilder<DB, TB, O> { | ||
return !!(qb as InsertQueryBuilder<DB, TB, O>).isInsertQueryBuilder | ||
} | ||
|
||
/** | ||
* A helper method to determine if a query builder object is of type {@link UpdateQueryBuilder}. | ||
* | ||
* Useful when using the {@link AnyQueryBuilder} type. | ||
*/ | ||
export function isUpdateQueryBuilder< | ||
DB, | ||
UT extends keyof DB, | ||
TB extends keyof DB, | ||
O | ||
>(qb: AnyQueryBuilder<DB, TB, O, UT>): qb is UpdateQueryBuilder<DB, UT, TB, O> { | ||
return !!(qb as UpdateQueryBuilder<DB, UT, TB, O>).isUpdateQueryBuilder | ||
} | ||
|
||
/** | ||
* A helper method to determine if a query builder object is of type {@link DeleteQueryBuilder}. | ||
* | ||
* Useful when using the {@link AnyQueryBuilder} type. | ||
*/ | ||
export function isDeleteQueryBuilder<DB, TB extends keyof DB, O>( | ||
qb: AnyQueryBuilder<DB, TB, O> | ||
): qb is DeleteQueryBuilder<DB, TB, O> { | ||
return !!(qb as DeleteQueryBuilder<DB, TB, O>).isDeleteQueryBuilder | ||
} | ||
|
||
/** | ||
* A helper method to determine if a query builder object is of type {@link MergeQueryBuilder}. | ||
* | ||
* Useful when using the {@link AnyQueryBuilder} type. | ||
*/ | ||
export function isMergeQueryBuilder< | ||
DB, | ||
TB extends keyof DB, | ||
O, | ||
ST extends keyof DB = any | ||
>( | ||
qb: AnyQueryBuilder<DB, TB, O, ST> | ||
): qb is typeof qb extends MergeQueryBuilder<DB, TB, O> | ||
? MergeQueryBuilder<DB, TB, O> | ||
: typeof qb extends WheneableMergeQueryBuilder<DB, TB, ST, O> | ||
? WheneableMergeQueryBuilder<DB, TB, ST, O> | ||
: typeof qb extends MatchedThenableMergeQueryBuilder<DB, TB, ST, TB | ST, O> | ||
? MatchedThenableMergeQueryBuilder<DB, TB, ST, TB | ST, O> | ||
: typeof qb extends NotMatchedThenableMergeQueryBuilder<DB, TB, ST, O> | ||
? NotMatchedThenableMergeQueryBuilder<DB, TB, ST, O> | ||
: never { | ||
return !!(qb as any).isMergeQueryBuilder | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { | ||
AnyQueryBuilder, | ||
MergeResult, | ||
isDeleteQueryBuilder, | ||
isInsertQueryBuilder, | ||
isMergeQueryBuilder, | ||
isSelectQueryBuilder, | ||
isUpdateQueryBuilder, | ||
} from '../../../' | ||
|
||
import { | ||
destroyTest, | ||
initTest, | ||
TestContext, | ||
expect, | ||
DIALECTS, | ||
Database, | ||
} from './test-setup.js' | ||
|
||
for (const dialect of DIALECTS) { | ||
describe(`${dialect}: query-utils`, () => { | ||
let ctx: TestContext | ||
|
||
before(async function () { | ||
ctx = await initTest(this, dialect) | ||
}) | ||
|
||
after(async () => { | ||
await destroyTest(ctx) | ||
}) | ||
|
||
it('should isSelectQueryBuilder be true', async () => { | ||
const query = ctx.db | ||
.selectFrom('person') | ||
.where('first_name', '=', 'Jennifer') | ||
|
||
expect(query.isSelectQueryBuilder).to.be.true | ||
expect(isSelectQueryBuilder(query)).to.be.true | ||
expect(isInsertQueryBuilder(query)).to.be.false | ||
expect(isUpdateQueryBuilder(query)).to.be.false | ||
expect(isDeleteQueryBuilder(query)).to.be.false | ||
expect(isMergeQueryBuilder(query)).to.be.false | ||
}) | ||
|
||
it('should isInsertQueryBuilder be true', async () => { | ||
const query = ctx.db.insertInto('person').values({ | ||
first_name: 'David', | ||
last_name: 'Bowie', | ||
gender: 'male', | ||
}) | ||
|
||
expect(query.isInsertQueryBuilder).to.be.true | ||
expect(isSelectQueryBuilder(query)).to.be.false | ||
expect(isInsertQueryBuilder(query)).to.be.true | ||
expect(isUpdateQueryBuilder(query)).to.be.false | ||
expect(isDeleteQueryBuilder(query)).to.be.false | ||
expect(isMergeQueryBuilder(query)).to.be.false | ||
}) | ||
|
||
it('should isUpdateQueryBuilder be true', async () => { | ||
const query = ctx.db | ||
.updateTable('person') | ||
.where('first_name', '=', 'John') | ||
.set({ last_name: 'Wick' }) | ||
|
||
expect(query.isUpdateQueryBuilder).to.be.true | ||
expect(isSelectQueryBuilder(query)).to.be.false | ||
expect(isInsertQueryBuilder(query)).to.be.false | ||
expect(isUpdateQueryBuilder(query)).to.be.true | ||
expect(isDeleteQueryBuilder(query)).to.be.false | ||
expect(isMergeQueryBuilder(query)).to.be.false | ||
}) | ||
|
||
it('should isDeleteQueryBuilder be true', async () => { | ||
const query = ctx.db.deleteFrom('person').where('first_name', '=', 'John') | ||
|
||
expect(query.isDeleteQueryBuilder).to.be.true | ||
expect(isSelectQueryBuilder(query)).to.be.false | ||
expect(isInsertQueryBuilder(query)).to.be.false | ||
expect(isUpdateQueryBuilder(query)).to.be.false | ||
expect(isDeleteQueryBuilder(query)).to.be.true | ||
expect(isMergeQueryBuilder(query)).to.be.false | ||
}) | ||
|
||
it('should isMergeQueryBuilder be true', async () => { | ||
// MergeQueryBuilder | ||
let query: AnyQueryBuilder<Database, 'person', MergeResult> = | ||
ctx.db.mergeInto('person') | ||
|
||
expect(query.isMergeQueryBuilder).to.be.true | ||
expect(isSelectQueryBuilder(query)).to.be.false | ||
expect(isInsertQueryBuilder(query)).to.be.false | ||
expect(isUpdateQueryBuilder(query)).to.be.false | ||
expect(isDeleteQueryBuilder(query)).to.be.false | ||
expect(isMergeQueryBuilder(query)).to.be.true | ||
|
||
// WheneableMergeQueryBuilder | ||
query = ctx.db | ||
.mergeInto('person') | ||
.using('pet', 'person.id', 'pet.owner_id') | ||
|
||
expect(query.isMergeQueryBuilder).to.be.true | ||
expect(isSelectQueryBuilder(query)).to.be.false | ||
expect(isInsertQueryBuilder(query)).to.be.false | ||
expect(isUpdateQueryBuilder(query)).to.be.false | ||
expect(isDeleteQueryBuilder(query)).to.be.false | ||
expect(isMergeQueryBuilder(query)).to.be.true | ||
|
||
// MatchedThenableMergeQueryBuilder | ||
query = ctx.db | ||
.mergeInto('person') | ||
.using('pet', 'person.id', 'pet.owner_id') | ||
.whenMatched() | ||
|
||
expect(query.isMergeQueryBuilder).to.be.true | ||
expect(isSelectQueryBuilder(query)).to.be.false | ||
expect(isInsertQueryBuilder(query)).to.be.false | ||
expect(isUpdateQueryBuilder(query)).to.be.false | ||
expect(isDeleteQueryBuilder(query)).to.be.false | ||
expect(isMergeQueryBuilder(query)).to.be.true | ||
|
||
// NotMatchedThenableMergeQueryBuilder | ||
query = ctx.db | ||
.mergeInto('person') | ||
.using('pet', 'person.id', 'pet.owner_id') | ||
.whenNotMatched() | ||
|
||
expect(query.isMergeQueryBuilder).to.be.true | ||
expect(isSelectQueryBuilder(query)).to.be.false | ||
expect(isInsertQueryBuilder(query)).to.be.false | ||
expect(isUpdateQueryBuilder(query)).to.be.false | ||
expect(isDeleteQueryBuilder(query)).to.be.false | ||
expect(isMergeQueryBuilder(query)).to.be.true | ||
}) | ||
}) | ||
} |
Oops, something went wrong.