如何延迟打字稿中的通用填充 - 对于 knex 查询?
How to defer generic filling in typescript - for knex queries?
好吧,我有一个如下所示的函数,可以根据字符串标识符加载多个数据库:
function getDB<TRecord extends {} = any, TResult = unknown[]>(config_name: string, maxConnections: number = 1): Knex<TRecord, TResult> {
if (!!cfg && !!cfg.datastores && !!cfg.datastores[config_name]) {
if (databases[config_name]) {
return databases[config_name];
} else {
const config = cfg.datastores[config_name];
const db = knex<TRecord, TResult>({
client: 'pg',
connection: config,
pool: {min: 0, max: maxConnections},
});
databases[config_name] = db;
return db;
}
} else {
throw new Error('config not initialized, can\'t load db');
}
}
databases
作为 Knex<any, any>[]
类型有点不安全,但现在可以忽略。
更烦人的是在使用这个函数时我会这样使用它:
const someDB = getDB('some');
const users = someDB("users").select('username').where('id', 10);
效果很好,机械上也很流畅。然而,打字被完全忽略了。现在我注意到 knex 支持基本的打字,我想添加这个 https://knexjs.org/#typescript-support 。我天真地会这样做;
interface User {
id: number;
username: string;
}
const someDB = getDB('some');
const users = someDB<User>("users").select('username').where('id', 10);
//expect users to be Pick<User, username>
但是这不起作用,typescript 仍然将用户视为 any
。这是因为 someDB
在调用 getDB
之后被赋予了正确的类型——这显然是 Knex<any, unknown[]>
。一种解决方案是在调用 getDB
:
时直接推断类型
interface User {
id: number;
username: string;
}
const someDB = getDB<User>('some');
const users = someDB("users").select('username').where('id', 10);
//expect users to be Pick<User, username>
然而,将代码扩展为:
interface User {
id: number;
username: string;
}
const someDB = getDB<User>('some');
const users = someDB("users").select('username').where('id', 10);
const students = someDB("student").select('name').where('id', 10);
这会产生错误,因为现在 students
也是 User[]
类型,显然不必如此。
那么,在调用 knex 函数之前,我将如何重构转发打字稿类型的功能?
function getDB(config_name: string, maxConnections: number = 1)
: <TRecord extends {} = any,
TResult = unknown[]>(
...params: Parameters<Knex<TRecord, TResult>>
) => ReturnType<Knex<TRecord, TResult>> {
// same implementation
}
将通用参数移至 return 类型。
(是的,我知道这很疯狂,我刚发现你可以这样做)
将一个函数定义为 return 类型,接受 2 个通用参数(TRecord,TResult),然后使用 Knex(应该是一个函数)定义一个接受 Knex 参数的函数,并且return使用相同函数的 return 类型。
稍后调用 someDb<User>('users')
时应正确转发类型信息。
好吧,我有一个如下所示的函数,可以根据字符串标识符加载多个数据库:
function getDB<TRecord extends {} = any, TResult = unknown[]>(config_name: string, maxConnections: number = 1): Knex<TRecord, TResult> {
if (!!cfg && !!cfg.datastores && !!cfg.datastores[config_name]) {
if (databases[config_name]) {
return databases[config_name];
} else {
const config = cfg.datastores[config_name];
const db = knex<TRecord, TResult>({
client: 'pg',
connection: config,
pool: {min: 0, max: maxConnections},
});
databases[config_name] = db;
return db;
}
} else {
throw new Error('config not initialized, can\'t load db');
}
}
databases
作为 Knex<any, any>[]
类型有点不安全,但现在可以忽略。
更烦人的是在使用这个函数时我会这样使用它:
const someDB = getDB('some');
const users = someDB("users").select('username').where('id', 10);
效果很好,机械上也很流畅。然而,打字被完全忽略了。现在我注意到 knex 支持基本的打字,我想添加这个 https://knexjs.org/#typescript-support 。我天真地会这样做;
interface User {
id: number;
username: string;
}
const someDB = getDB('some');
const users = someDB<User>("users").select('username').where('id', 10);
//expect users to be Pick<User, username>
但是这不起作用,typescript 仍然将用户视为 any
。这是因为 someDB
在调用 getDB
之后被赋予了正确的类型——这显然是 Knex<any, unknown[]>
。一种解决方案是在调用 getDB
:
interface User {
id: number;
username: string;
}
const someDB = getDB<User>('some');
const users = someDB("users").select('username').where('id', 10);
//expect users to be Pick<User, username>
然而,将代码扩展为:
interface User {
id: number;
username: string;
}
const someDB = getDB<User>('some');
const users = someDB("users").select('username').where('id', 10);
const students = someDB("student").select('name').where('id', 10);
这会产生错误,因为现在 students
也是 User[]
类型,显然不必如此。
那么,在调用 knex 函数之前,我将如何重构转发打字稿类型的功能?
function getDB(config_name: string, maxConnections: number = 1)
: <TRecord extends {} = any,
TResult = unknown[]>(
...params: Parameters<Knex<TRecord, TResult>>
) => ReturnType<Knex<TRecord, TResult>> {
// same implementation
}
将通用参数移至 return 类型。 (是的,我知道这很疯狂,我刚发现你可以这样做)
将一个函数定义为 return 类型,接受 2 个通用参数(TRecord,TResult),然后使用 Knex
稍后调用 someDb<User>('users')
时应正确转发类型信息。