技术讲座:Knex.js 的泛型增强:SQL 构建器的类型提示
引言
Knex.js 是一个强大的 SQL 构建器,它允许开发者以声明式的方式构建 SQL 查询。然而,在实际开发中,类型安全是一个至关重要的考虑因素。在本文中,我们将探讨如何通过泛型增强 Knex.js,使其具备类型提示功能,从而提高代码的可读性和安全性。
Knex.js 简介
Knex.js 是一个用于构建 SQL 查询的库,它支持多种数据库后端,如 MySQL、PostgreSQL、SQLite 等。Knex.js 允许开发者以声明式的方式编写 SQL 查询,这使得代码更加简洁易读。
const knex = require('knex')({
client: 'mysql',
connection: {
host: '127.0.0.1',
user: 'root',
password: '',
database: 'test'
}
});
knex.select('id', 'name').from('users').then(users => {
console.log(users);
});
泛型增强 Knex.js
为了实现 Knex.js 的泛型增强,我们需要定义一些泛型类型,以表示 Knex.js 中不同类型的查询参数。
1. 定义泛型类型
首先,我们需要定义一些泛型类型,以表示 Knex.js 中不同类型的查询参数。
type QueryType = 'select' | 'insert' | 'update' | 'delete';
interface QueryParams<T> {
table: string;
columns?: (keyof T)[];
values?: T;
where?: { [key in keyof T]?: any };
order?: { [key in keyof T]?: 'asc' | 'desc' };
}
2. 实现泛型查询函数
接下来,我们需要实现一个泛型查询函数,该函数接受查询参数,并返回一个 Promise 对象。
function query<T>(knex: KnexInstance, type: QueryType, params: QueryParams<T>): Promise<T[]> {
return new Promise((resolve, reject) => {
try {
switch (type) {
case 'select':
const queryStr = `SELECT ${params.columns?.join(', ')} FROM ${params.table}`;
knex.raw(queryStr).then(result => resolve(result.rows));
break;
case 'insert':
const insertStr = `INSERT INTO ${params.table} (${params.columns?.join(', ')}) VALUES (${params.columns?.map(() => '?').join(', ')})`;
knex.raw(insertStr, params.values).then(result => resolve(result.rowCount));
break;
case 'update':
const updateStr = `UPDATE ${params.table} SET ${params.columns?.map((key) => `${key} = ?`).join(', ')} WHERE ${params.where?.map((key, index) => `${key} = ?`).join(' AND ')}`;
knex.raw(updateStr, params.values).then(result => resolve(result.rowCount));
break;
case 'delete':
const deleteStr = `DELETE FROM ${params.table} WHERE ${params.where?.map((key, index) => `${key} = ?`).join(' AND ')}`;
knex.raw(deleteStr, params.values).then(result => resolve(result.rowCount));
break;
default:
reject(new Error('Unsupported query type'));
}
} catch (error) {
reject(error);
}
});
}
3. 使用泛型查询函数
现在,我们可以使用泛型查询函数来执行各种类型的查询。
interface User {
id: number;
name: string;
age: number;
}
const knex = require('knex')({
client: 'mysql',
connection: {
host: '127.0.0.1',
user: 'root',
password: '',
database: 'test'
}
});
// 查询用户列表
query<User>(knex, 'select', {
table: 'users',
columns: ['id', 'name', 'age']
}).then(users => {
console.log(users);
});
// 插入新用户
query<User>(knex, 'insert', {
table: 'users',
columns: ['name', 'age'],
values: { name: 'John Doe', age: 30 }
}).then(result => {
console.log(`Inserted ${result} row(s).`);
});
// 更新用户信息
query<User>(knex, 'update', {
table: 'users',
columns: ['name', 'age'],
values: { name: 'Jane Doe', age: 25 },
where: { id: 1 }
}).then(result => {
console.log(`Updated ${result} row(s).`);
});
// 删除用户
query<User>(knex, 'delete', {
table: 'users',
where: { id: 1 }
}).then(result => {
console.log(`Deleted ${result} row(s).`);
});
总结
通过泛型增强 Knex.js,我们可以为 SQL 构建器提供类型提示功能,从而提高代码的可读性和安全性。在实际开发中,我们可以根据需要扩展泛型类型和查询函数,以支持更多类型的查询操作。
代码示例
以下是本文中使用的代码示例:
type QueryType = 'select' | 'insert' | 'update' | 'delete';
interface QueryParams<T> {
table: string;
columns?: (keyof T)[];
values?: T;
where?: { [key in keyof T]?: any };
order?: { [key in keyof T]?: 'asc' | 'desc' };
}
function query<T>(knex: KnexInstance, type: QueryType, params: QueryParams<T>): Promise<T[]> {
return new Promise((resolve, reject) => {
try {
switch (type) {
case 'select':
const queryStr = `SELECT ${params.columns?.join(', ')} FROM ${params.table}`;
knex.raw(queryStr).then(result => resolve(result.rows));
break;
case 'insert':
const insertStr = `INSERT INTO ${params.table} (${params.columns?.join(', ')}) VALUES (${params.columns?.map(() => '?').join(', ')})`;
knex.raw(insertStr, params.values).then(result => resolve(result.rowCount));
break;
case 'update':
const updateStr = `UPDATE ${params.table} SET ${params.columns?.map((key) => `${key} = ?`).join(', ')} WHERE ${params.where?.map((key, index) => `${key} = ?`).join(' AND ')}`;
knex.raw(updateStr, params.values).then(result => resolve(result.rowCount));
break;
case 'delete':
const deleteStr = `DELETE FROM ${params.table} WHERE ${params.where?.map((key, index) => `${key} = ?`).join(' AND ')}`;
knex.raw(deleteStr, params.values).then(result => resolve(result.rowCount));
break;
default:
reject(new Error('Unsupported query type'));
}
} catch (error) {
reject(error);
}
});
}
interface User {
id: number;
name: string;
age: number;
}
const knex = require('knex')({
client: 'mysql',
connection: {
host: '127.0.0.1',
user: 'root',
password: '',
database: 'test'
}
});
// 查询用户列表
query<User>(knex, 'select', {
table: 'users',
columns: ['id', 'name', 'age']
}).then(users => {
console.log(users);
});
// 插入新用户
query<User>(knex, 'insert', {
table: 'users',
columns: ['name', 'age'],
values: { name: 'John Doe', age: 30 }
}).then(result => {
console.log(`Inserted ${result} row(s).`);
});
// 更新用户信息
query<User>(knex, 'update', {
table: 'users',
columns: ['name', 'age'],
values: { name: 'Jane Doe', age: 25 },
where: { id: 1 }
}).then(result => {
console.log(`Updated ${result} row(s).`);
});
// 删除用户
query<User>(knex, 'delete', {
table: 'users',
where: { id: 1 }
}).then(result => {
console.log(`Deleted ${result} row(s).`);
});
总结
通过泛型增强 Knex.js,我们可以为 SQL 构建器提供类型提示功能,从而提高代码的可读性和安全性。在实际开发中,我们可以根据需要扩展泛型类型和查询函数,以支持更多类型的查询操作。