Skip to content

Commit 177938a

Browse files
authored
Introduce the "infamous triplet" export (knex#4181)
1 parent 2b1fed5 commit 177938a

11 files changed

+681
-603
lines changed

.eslintignore

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
*.stub
2-
#
3-
lib/util/import-file.js
4-
test/jake-util/knexfile-imports
2+
#
3+
lib/migrations/util/import-file.js
4+
test/jake-util/knexfile-imports

.github/workflows/linting.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22

3-
name: Linting
3+
name: Linting and Types
44

55
on:
66
push:
@@ -13,7 +13,7 @@ on:
1313
jobs:
1414
build:
1515
runs-on: ubuntu-latest
16-
name: Linting
16+
name: Linting and Types
1717

1818
strategy:
1919
matrix:
@@ -35,6 +35,6 @@ jobs:
3535
run: npm install
3636

3737
- name: Run lint:everything
38-
run: echo npm run lint:everything
38+
run: npm run lint:everything
3939
env:
4040
CI: true

README.md

+28
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,31 @@ try {
8888
console.error(e);
8989
};
9090
```
91+
92+
## TypeScript example
93+
```
94+
import { Knex, knex } from 'knex'
95+
96+
interface User {
97+
id: number;
98+
age: number;
99+
name: string;
100+
active: boolean;
101+
departmentId: number;
102+
}
103+
104+
const config: Knex.Config = {
105+
client: 'sqlite3',
106+
connection: {
107+
filename: './data.db',
108+
},
109+
});
110+
111+
const knexInstance = knex(config);
112+
113+
try {
114+
const users = await knex<User>('users').select('id', 'age');
115+
} catch (err) {
116+
// error handling
117+
}
118+
```

UPGRADING.md

+10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
### Upgrading to version 0.95.0+
44

5+
* TypeScript type exports changed significantly. While `import Knex from 'knex';` used to import the knex instantiation function, the namespace and the interface for the knex instantiation function/object, there is now a clear distinction between them:
6+
```
7+
import { knex } from 'knex' // this is a function that you call to instantiate knex
8+
import { Knex } from 'knex' // this is a namespace, and a type of a knex object
9+
import KnexTimeoutError = Knex.KnexTimeoutError; // this is a class from the Knex namespace
10+
11+
const config: Knex.Config = {} // this is a type from the Knex namespace
12+
const knexInstance: Knex = knex(config)
13+
```
14+
515
* Connection url parsing changed from legacy [url.parse](https://nodejs.org/docs/latest-v10.x/api/url.html#url_legacy_url_api) to [WHATWG URL](https://nodejs.org/docs/latest-v10.x/api/url.html#url_the_whatwg_url_api). If you have symbols, unusual for a URL (not A-z, not digits, not dot, not dash) - check [Node.js docs](https://nodejs.org/docs/latest-v10.x/api/url.html#url_percent_encoding_in_urls) for details
616

717
* `Knex.raw` support dropped, use `knex.raw` (`require('knex').raw()` won't work anymore)

knex.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,19 @@
55
// For details and documentation:
66
// http://knexjs.org
77

8-
module.exports = require('./lib/index');
8+
const knex = require('./lib/index');
9+
10+
/**
11+
* These export configurations enable JS and TS developers
12+
* to consume knex in whatever way best suits their needs.
13+
* Some examples of supported import syntax includes:
14+
* - `const knex = require('knex')`
15+
* - `const { knex } = require('knex')`
16+
* - `import * as knex from 'knex'`
17+
* - `import { knex } from 'knex'`
18+
* - `import knex from 'knex'`
19+
*/
20+
knex.knex = knex;
21+
knex.default = knex;
22+
23+
module.exports = knex;

test-tsd/common.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
export const clientConfig = {
1+
import type { Knex } from "../types";
2+
3+
export const clientConfig: Knex.Config = {
24
client: 'sqlite3',
35
connection: {
46
filename: './mydb.sqlite',

test-tsd/querybuilder.test-d.ts

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
import Knex from '../types';
1+
import { Knex, knex } from '../types';
22
import { expectType } from 'tsd';
33
import { clientConfig } from './common';
44

5-
const knex = Knex(clientConfig);
5+
const knexInstance = knex(clientConfig);
66

77
// Use:
8-
// import Knex from 'knex'
9-
// when "esModuleInterop": true
8+
// import { Knex } from 'knex'
109

1110
// This would be `declare module 'knex'` in runtime code
1211
declare module '../types' {
13-
interface QueryBuilder {
14-
customSelect<TRecord, TResult>(
15-
value: number
16-
): QueryBuilder<TRecord, TResult>;
12+
namespace Knex {
13+
interface QueryBuilder {
14+
customSelect<TRecord, TResult>(
15+
value: number
16+
): Knex.QueryBuilder<TRecord, TResult>;
17+
}
1718
}
1819
}
1920

@@ -22,5 +23,5 @@ Knex.QueryBuilder.extend('customSelect', function (value: number) {
2223
});
2324

2425
const main = async () => {
25-
expectType<number[]>(await knex('users').customSelect<any, number[]>(42));
26+
expectType<number[]>(await knexInstance('users').customSelect<any, number[]>(42));
2627
};

test-tsd/tables.test-d.ts

+17-17
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import Knex from '../types';
1+
import { knex, Knex } from '../types';
22
import { clientConfig } from './common';
33
import { expectType } from 'tsd';
44

5-
const knex = Knex(clientConfig);
5+
const knexInstance = knex(clientConfig);
66

77
interface User {
88
id: number;
@@ -57,39 +57,39 @@ declare module '../types/tables' {
5757
const main = async () => {
5858
// # Select:
5959

60-
expectType<any[]>(await knex('users'));
60+
expectType<any[]>(await knexInstance('users'));
6161

6262
// This test (others similar to it) may seem useless but they are needed
6363
// to test for left-to-right inference issues eg: #3260
64-
expectType<User[]>(await knex('users'));
65-
expectType<User[]>(await knex<User>('users'));
66-
expectType<User[]>(await knex('users_inferred'));
67-
expectType<User[]>(await knex('users_composite'));
64+
expectType<User[]>(await knexInstance('users'));
65+
expectType<User[]>(await knexInstance<User>('users'));
66+
expectType<User[]>(await knexInstance('users_inferred'));
67+
expectType<User[]>(await knexInstance('users_composite'));
6868

69-
expectType<any[]>(await knex('users').select('id'));
70-
expectType<Partial<User>[]>(await knex('users').select('id'));
69+
expectType<any[]>(await knexInstance('users').select('id'));
70+
expectType<Partial<User>[]>(await knexInstance('users').select('id'));
7171

72-
expectType<Pick<User, 'id'>[]>(await knex('users_inferred').select('id'));
73-
expectType<Pick<User, 'id'>[]>(await knex('users_composite').select('id'));
72+
expectType<Pick<User, 'id'>[]>(await knexInstance('users_inferred').select('id'));
73+
expectType<Pick<User, 'id'>[]>(await knexInstance('users_composite').select('id'));
7474
expectType<Pick<User, 'id' | 'age'>[]>(
75-
await knex('users_inferred').select('id').select('age')
75+
await knexInstance('users_inferred').select('id').select('age')
7676
);
7777

7878
expectType<Pick<User, 'id' | 'age'>[]>(
79-
await knex('users_composite').select('id').select('age')
79+
await knexInstance('users_composite').select('id').select('age')
8080
);
8181

8282
expectType<Pick<User, 'id' | 'age'>[]>(
83-
await knex('users_inferred').select('id', 'age')
83+
await knexInstance('users_inferred').select('id', 'age')
8484
);
8585
expectType<Pick<User, 'id' | 'age'>[]>(
86-
await knex('users_composite').select('id', 'age')
86+
await knexInstance('users_composite').select('id', 'age')
8787
);
8888

8989
expectType<Pick<User, 'id'> | undefined>(
90-
await knex.first('id').from('users_inferred')
90+
await knexInstance.first('id').from('users_inferred')
9191
);
9292
expectType<Pick<User, 'id'> | undefined>(
93-
await knex.first('id').from('users_composite')
93+
await knexInstance.first('id').from('users_composite')
9494
);
9595
};

test-tsd/types.test-d.ts

+27-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
11
import { expectAssignable, expectType } from 'tsd';
2-
import Knex, { QueryBuilder } from '../types';
32
import { clientConfig } from './common';
43

5-
const knex = Knex(clientConfig);
4+
import knexDefault, { Knex, knex } from '../types';
5+
import * as knexStar from '../types';
6+
import knexCjsImport = require('../');
7+
import QueryBuilder = Knex.QueryBuilder;
8+
import KnexTimeoutError = Knex.KnexTimeoutError;
9+
10+
const knexCjs = require('../knex');
11+
const { knex: knexCjsNamed } = require('../knex');
12+
13+
expectType<Knex<any, unknown[]>>(knexDefault({}));
14+
expectType<Knex<any, unknown[]>>(knex({}));
15+
expectType<Knex<any, unknown[]>>(knexStar.default({}));
16+
expectType<Knex<any, unknown[]>>(knexStar.knex({}));
17+
expectType<Knex<any, unknown[]>>(knexCjsImport.default({}));
18+
expectType<Knex<any, unknown[]>>(knexCjsImport.knex({}));
19+
expectType<KnexTimeoutError>(new KnexTimeoutError());
20+
expectType<KnexTimeoutError>(new KnexTimeoutError());
21+
22+
// eslint-disable-next-line
23+
expectType<any>(knexCjs({}));
24+
// eslint-disable-next-line
25+
expectType<any>(knexCjsNamed({}));
26+
27+
const knexInstance = knexDefault(clientConfig);
628

729
// ToDo remove this copy-pasted type after we can export it as a named properly
830
type DeferredKeySelection<
@@ -52,7 +74,7 @@ expectType<
5274
DeferredKeySelection<User, never, false, {}, false, {}, never>[]
5375
>
5476
>(
55-
knex
77+
knexInstance
5678
.table<User>('users')
5779
.insert({ id: 10, active: true })
5880
.onConflict('id')
@@ -61,7 +83,7 @@ expectType<
6183
);
6284

6385
expectAssignable<QueryBuilder>(
64-
knex
86+
knexInstance
6587
.insert({ col: 'x' })
6688
.into('table')
6789
.onConflict('col')
@@ -70,7 +92,7 @@ expectAssignable<QueryBuilder>(
7092
);
7193

7294
expectAssignable<QueryBuilder>(
73-
knex
95+
knexInstance
7496
.insert({ id: 10, active: true })
7597
.into('table')
7698
.onConflict(['id'])

types/index.d.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -335,9 +335,9 @@ interface TransactionConfig {
335335
userParams?: Record<string, any>;
336336
doNotRejectOnRollback?: boolean;
337337
connection?: any;
338-
};
338+
}
339339

340-
interface Knex<TRecord extends {} = any, TResult = unknown[]>
340+
export interface Knex<TRecord extends {} = any, TResult = unknown[]>
341341
extends Knex.QueryInterface<TRecord, TResult>, events.EventEmitter {
342342
<TTable extends Knex.TableNames>(
343343
tableName: TTable,
@@ -393,11 +393,11 @@ interface Knex<TRecord extends {} = any, TResult = unknown[]>
393393
withUserParams(params: Record<string, any>): Knex;
394394
}
395395

396-
declare function Knex<TRecord extends {} = any, TResult = unknown[]>(
396+
export declare function knex<TRecord extends {} = any, TResult = unknown[]>(
397397
config: Knex.Config | string
398398
): Knex<TRecord, TResult>;
399399

400-
declare namespace Knex {
400+
export declare namespace Knex {
401401
//
402402
// Utility Types
403403
//
@@ -2261,4 +2261,4 @@ declare namespace Knex {
22612261
export class KnexTimeoutError extends Error {}
22622262
}
22632263

2264-
export = Knex;
2264+
export default knex;

0 commit comments

Comments
 (0)