Introduce the "infamous triplet" export (#4181)

This commit is contained in:
Igor Savin 2020-12-30 22:26:08 +02:00 committed by GitHub
parent 2b1fed5434
commit 177938afeb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 681 additions and 603 deletions

View File

@ -1,4 +1,4 @@
*.stub
#
lib/util/import-file.js
test/jake-util/knexfile-imports
#
lib/migrations/util/import-file.js
test/jake-util/knexfile-imports

View File

@ -1,6 +1,6 @@
---
name: Linting
name: Linting and Types
on:
push:
@ -13,7 +13,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
name: Linting
name: Linting and Types
strategy:
matrix:
@ -35,6 +35,6 @@ jobs:
run: npm install
- name: Run lint:everything
run: echo npm run lint:everything
run: npm run lint:everything
env:
CI: true

View File

@ -88,3 +88,31 @@ try {
console.error(e);
};
```
## TypeScript example
```
import { Knex, knex } from 'knex'
interface User {
id: number;
age: number;
name: string;
active: boolean;
departmentId: number;
}
const config: Knex.Config = {
client: 'sqlite3',
connection: {
filename: './data.db',
},
});
const knexInstance = knex(config);
try {
const users = await knex<User>('users').select('id', 'age');
} catch (err) {
// error handling
}
```

View File

@ -2,6 +2,16 @@
### Upgrading to version 0.95.0+
* 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:
```
import { knex } from 'knex' // this is a function that you call to instantiate knex
import { Knex } from 'knex' // this is a namespace, and a type of a knex object
import KnexTimeoutError = Knex.KnexTimeoutError; // this is a class from the Knex namespace
const config: Knex.Config = {} // this is a type from the Knex namespace
const knexInstance: Knex = knex(config)
```
* 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
* `Knex.raw` support dropped, use `knex.raw` (`require('knex').raw()` won't work anymore)

17
knex.js
View File

@ -5,4 +5,19 @@
// For details and documentation:
// http://knexjs.org
module.exports = require('./lib/index');
const knex = require('./lib/index');
/**
* These export configurations enable JS and TS developers
* to consume knex in whatever way best suits their needs.
* Some examples of supported import syntax includes:
* - `const knex = require('knex')`
* - `const { knex } = require('knex')`
* - `import * as knex from 'knex'`
* - `import { knex } from 'knex'`
* - `import knex from 'knex'`
*/
knex.knex = knex;
knex.default = knex;
module.exports = knex;

View File

@ -1,4 +1,6 @@
export const clientConfig = {
import type { Knex } from "../types";
export const clientConfig: Knex.Config = {
client: 'sqlite3',
connection: {
filename: './mydb.sqlite',

View File

@ -1,19 +1,20 @@
import Knex from '../types';
import { Knex, knex } from '../types';
import { expectType } from 'tsd';
import { clientConfig } from './common';
const knex = Knex(clientConfig);
const knexInstance = knex(clientConfig);
// Use:
// import Knex from 'knex'
// when "esModuleInterop": true
// import { Knex } from 'knex'
// This would be `declare module 'knex'` in runtime code
declare module '../types' {
interface QueryBuilder {
customSelect<TRecord, TResult>(
value: number
): QueryBuilder<TRecord, TResult>;
namespace Knex {
interface QueryBuilder {
customSelect<TRecord, TResult>(
value: number
): Knex.QueryBuilder<TRecord, TResult>;
}
}
}
@ -22,5 +23,5 @@ Knex.QueryBuilder.extend('customSelect', function (value: number) {
});
const main = async () => {
expectType<number[]>(await knex('users').customSelect<any, number[]>(42));
expectType<number[]>(await knexInstance('users').customSelect<any, number[]>(42));
};

View File

@ -1,8 +1,8 @@
import Knex from '../types';
import { knex, Knex } from '../types';
import { clientConfig } from './common';
import { expectType } from 'tsd';
const knex = Knex(clientConfig);
const knexInstance = knex(clientConfig);
interface User {
id: number;
@ -57,39 +57,39 @@ declare module '../types/tables' {
const main = async () => {
// # Select:
expectType<any[]>(await knex('users'));
expectType<any[]>(await knexInstance('users'));
// This test (others similar to it) may seem useless but they are needed
// to test for left-to-right inference issues eg: #3260
expectType<User[]>(await knex('users'));
expectType<User[]>(await knex<User>('users'));
expectType<User[]>(await knex('users_inferred'));
expectType<User[]>(await knex('users_composite'));
expectType<User[]>(await knexInstance('users'));
expectType<User[]>(await knexInstance<User>('users'));
expectType<User[]>(await knexInstance('users_inferred'));
expectType<User[]>(await knexInstance('users_composite'));
expectType<any[]>(await knex('users').select('id'));
expectType<Partial<User>[]>(await knex('users').select('id'));
expectType<any[]>(await knexInstance('users').select('id'));
expectType<Partial<User>[]>(await knexInstance('users').select('id'));
expectType<Pick<User, 'id'>[]>(await knex('users_inferred').select('id'));
expectType<Pick<User, 'id'>[]>(await knex('users_composite').select('id'));
expectType<Pick<User, 'id'>[]>(await knexInstance('users_inferred').select('id'));
expectType<Pick<User, 'id'>[]>(await knexInstance('users_composite').select('id'));
expectType<Pick<User, 'id' | 'age'>[]>(
await knex('users_inferred').select('id').select('age')
await knexInstance('users_inferred').select('id').select('age')
);
expectType<Pick<User, 'id' | 'age'>[]>(
await knex('users_composite').select('id').select('age')
await knexInstance('users_composite').select('id').select('age')
);
expectType<Pick<User, 'id' | 'age'>[]>(
await knex('users_inferred').select('id', 'age')
await knexInstance('users_inferred').select('id', 'age')
);
expectType<Pick<User, 'id' | 'age'>[]>(
await knex('users_composite').select('id', 'age')
await knexInstance('users_composite').select('id', 'age')
);
expectType<Pick<User, 'id'> | undefined>(
await knex.first('id').from('users_inferred')
await knexInstance.first('id').from('users_inferred')
);
expectType<Pick<User, 'id'> | undefined>(
await knex.first('id').from('users_composite')
await knexInstance.first('id').from('users_composite')
);
};

View File

@ -1,8 +1,30 @@
import { expectAssignable, expectType } from 'tsd';
import Knex, { QueryBuilder } from '../types';
import { clientConfig } from './common';
const knex = Knex(clientConfig);
import knexDefault, { Knex, knex } from '../types';
import * as knexStar from '../types';
import knexCjsImport = require('../');
import QueryBuilder = Knex.QueryBuilder;
import KnexTimeoutError = Knex.KnexTimeoutError;
const knexCjs = require('../knex');
const { knex: knexCjsNamed } = require('../knex');
expectType<Knex<any, unknown[]>>(knexDefault({}));
expectType<Knex<any, unknown[]>>(knex({}));
expectType<Knex<any, unknown[]>>(knexStar.default({}));
expectType<Knex<any, unknown[]>>(knexStar.knex({}));
expectType<Knex<any, unknown[]>>(knexCjsImport.default({}));
expectType<Knex<any, unknown[]>>(knexCjsImport.knex({}));
expectType<KnexTimeoutError>(new KnexTimeoutError());
expectType<KnexTimeoutError>(new KnexTimeoutError());
// eslint-disable-next-line
expectType<any>(knexCjs({}));
// eslint-disable-next-line
expectType<any>(knexCjsNamed({}));
const knexInstance = knexDefault(clientConfig);
// ToDo remove this copy-pasted type after we can export it as a named properly
type DeferredKeySelection<
@ -52,7 +74,7 @@ expectType<
DeferredKeySelection<User, never, false, {}, false, {}, never>[]
>
>(
knex
knexInstance
.table<User>('users')
.insert({ id: 10, active: true })
.onConflict('id')
@ -61,7 +83,7 @@ expectType<
);
expectAssignable<QueryBuilder>(
knex
knexInstance
.insert({ col: 'x' })
.into('table')
.onConflict('col')
@ -70,7 +92,7 @@ expectAssignable<QueryBuilder>(
);
expectAssignable<QueryBuilder>(
knex
knexInstance
.insert({ id: 10, active: true })
.into('table')
.onConflict(['id'])

10
types/index.d.ts vendored
View File

@ -335,9 +335,9 @@ interface TransactionConfig {
userParams?: Record<string, any>;
doNotRejectOnRollback?: boolean;
connection?: any;
};
}
interface Knex<TRecord extends {} = any, TResult = unknown[]>
export interface Knex<TRecord extends {} = any, TResult = unknown[]>
extends Knex.QueryInterface<TRecord, TResult>, events.EventEmitter {
<TTable extends Knex.TableNames>(
tableName: TTable,
@ -393,11 +393,11 @@ interface Knex<TRecord extends {} = any, TResult = unknown[]>
withUserParams(params: Record<string, any>): Knex;
}
declare function Knex<TRecord extends {} = any, TResult = unknown[]>(
export declare function knex<TRecord extends {} = any, TResult = unknown[]>(
config: Knex.Config | string
): Knex<TRecord, TResult>;
declare namespace Knex {
export declare namespace Knex {
//
// Utility Types
//
@ -2261,4 +2261,4 @@ declare namespace Knex {
export class KnexTimeoutError extends Error {}
}
export = Knex;
export default knex;

File diff suppressed because it is too large Load Diff