Knex.js 的泛型增强:如何让 SQL 构建器具备类型提示

技术讲座: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 构建器提供类型提示功能,从而提高代码的可读性和安全性。在实际开发中,我们可以根据需要扩展泛型类型和查询函数,以支持更多类型的查询操作。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注