讲师中心 微信公众号
AI工具推荐 视频效率加速

在TypeScript中将SQLite查询结果映射到类型化对象

夜婷大大_3456

夜婷大大_3456

发布时间:2025-11-14 12:53:39

|

344人浏览过

|

来源于php中文网

原创

在TypeScript中将SQLite查询结果映射到类型化对象

本教程旨在解决在electron或node.js环境中使用`sqlite3`时,如何将异步sqlite查询结果正确地反序列化并映射到typescript强类型对象的问题。文章将详细解释`sqlite3.all()`方法的异步特性,演示如何利用promise封装回调,并指导开发者使用`for...of`循环安全高效地将数据库行数据转换为自定义的typescript类实例,从而提升代码的类型安全性和可维护性。

引言:SQLite查询结果到TypeScript对象的挑战

在基于Node.js或Electron的应用程序中,使用sqlite3库与SQLite数据库交互是常见的需求。当从数据库中查询数据时,通常会得到一个原始的数据行数组。然而,为了更好地利用TypeScript的类型系统,并提高代码的可读性和健壮性,我们通常希望将这些原始数据行转换为预定义的TypeScript类或接口的实例。

直接将数据库查询结果映射到TypeScript对象可能会遇到一些挑战,特别是涉及到sqlite3库的异步操作方式以及JavaScript中循环迭代的细微差别。本教程将深入探讨这些问题,并提供一个清晰、专业的解决方案。

理解sqlite3.all()的异步特性

sqlite3库中的大多数数据库操作都是异步的,这意味着它们不会立即返回结果。db.prepare().all()方法也不例外。根据node-sqlite3的API文档,all()方法接受一个回调函数作为参数,当查询完成并获取所有结果行时,该回调函数会被调用。all()方法本身返回的是Statement对象,用于方法链式调用,而不是查询结果本身。

这意味着,如果你尝试像同步代码那样直接在query.all()之后访问结果,你将无法获取到数据,因为数据仍在异步处理中。为了正确处理这种异步行为,我们需要采用适当的异步编程模式,例如回调函数或Promise。

使用Promise封装异步操作

在现代JavaScript和TypeScript开发中,Promise是处理异步操作的首选方式,它能使异步代码的结构更清晰,避免“回调地狱”。我们可以将query.all()的基于回调的API封装在一个Promise中,使其返回一个Promise对象,这样我们就可以使用.then()或async/await来处理结果。

以下是封装GetAllObjs函数以返回一个Promise的示例:

Scrapfly SDK TypeScript
Scrapfly SDK TypeScript

使用 Scrapfly SDK(scrapfly-sdk)在 TypeScript 或 JavaScript 中进行网页抓取,用于用户请求抓取 URL、绕过反爬虫防护、渲染页面等场景。

下载
import * as sqlite3 from 'sqlite3'; // 假设你已经配置了sqlite3

// 定义Obj接口,与数据库表结构匹配
interface Obj {
  id: number;
  name: string;
  amount: number;
}

// 假设db是一个已连接的sqlite3数据库实例
// const db = new sqlite3.Database(':memory:'); 

/**
 * 从ObjTable中获取所有对象并返回一个Promise
 * 该Promise解析为一个Obj数组
 */
export const GetAllObjs = (): Promise<Obj[]> => {
  const query = db.prepare("SELECT * FROM ObjTable");

  return new Promise((resolve, reject) => {
    query.all((err: Error | null, rows: any[]) => {
      if (err) {
        // 如果发生错误,拒绝Promise
        return reject(err);
      }

      const objs: Obj[] = [];
      // 使用 for...of 循环正确迭代行数据
      for (const row of rows) {
        // 将每一行数据映射到Obj接口
        objs.push({
          id: row.id,
          name: row.name,
          amount: row.amount,
        });
      }
      // 解析Promise,返回Obj数组
      resolve(objs);
    });
  });
};

正确迭代查询结果并构建TypeScript对象

在上述Promise封装中,一个关键的改进是使用了for (const row of rows)循环来迭代查询结果。

避免for...in的陷阱: 原始问题中可能出现的错误是使用for (const row in rows as Obj[])。for...in循环设计用于遍历对象的可枚举属性名称(键),而不是数组的元素值。对于数组,for...in会遍历其索引(如"0", "1", "2"等字符串),而不是数组中实际的对象。因此,row变量将是一个字符串索引,而非数据库行对象,导致row.id等访问失败。

使用for...of进行迭代:for...of循环是ES6引入的,专门用于遍历可迭代对象(如数组、Map、Set等)的元素值。当对rows数组使用for...of时,每次迭代的row变量将直接是数组中的一个数据行对象。这样,你就可以安全地通过row.columnName的方式访问每个列的值,并将其赋值给TypeScript对象的相应属性。

在回调函数内部,rows参数是一个any[]类型的数组,其中每个元素都是一个表示数据库行的JavaScript对象。这些对象的属性名与数据库表的列名相匹配。因此,我们可以直接通过row.id、row.name、row.amount等方式访问这些值,并构造新的Obj实例。

完整示例代码

为了提供更完整的上下文,以下是一个包含Obj接口定义、表创建和数据获取的综合示例:

import * as sqlite3 from 'sqlite3';

// 假设db是一个已连接的sqlite3数据库实例
// 通常在Electron主进程或Node.js服务启动时初始化
const db = new sqlite3.Database('./mydb.sqlite', (err) => {
  if (err) {
    console.error("Error opening database", err);
  } else {
    console.log("Database connected.");
  }
});

// 1. 定义Obj接口,与数据库表结构匹配
interface Obj {
  id: number;
  name: string;
  amount: number;
}

// 2. 表创建函数
export const CreateObjTable = (): Promise<sqlite3.RunResult> => {
  return new Promise((resolve, reject) => {
    const query = db.prepare(`
      CREATE TABLE IF NOT EXISTS ObjTable
      (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT,
        amount INTEGER
      )
    `);
    query.run((err: Error | null, result: sqlite3.RunResult) => {
      if (err) {
        return reject(err);
      }
      resolve(result);
    });
  });
};

// 3. 插入数据示例(可选,用于测试)
export const InsertObj = (name: string, amount: number): Promise<sqlite3.RunResult> => {
  return new Promise((resolve, reject) => {
    const query = db.prepare("INSERT INTO ObjTable (name, amount) VALUES (?, ?)");
    query.run(name, amount, function(err: Error | null) {
      if (err) {
        return reject(err);
      }
      // this.lastID 可以获取插入的ID
      resolve(this); 
    });
  });
};

// 4. 获取所有对象并反序列化为Obj[]
export const GetAllObjs = (): Promise<Obj[]> => {
  const query = db.prepare("SELECT * FROM ObjTable");

  return new Promise((resolve, reject) => {
    query.all((err: Error | null, rows: any[]) => {
      if (err) {
        return reject(err);
      }

      const objs: Obj[] = [];
      for (const row of rows) { // 正确使用 for...of 迭代
        objs.push({
          id: row.id,
          name: row.name,
          amount: row.amount,
        });
      }
      resolve(objs);
    });
  });
};

// 使用示例 (在async函数中或Promise链中调用)
(async () => {
  try {
    await CreateObjTable();
    console.log("ObjTable created or already exists.");

    await InsertObj("Item A", 100);
    await InsertObj("Item B", 250);
    console.log("Sample data inserted.");

    const allObjs = await GetAllObjs();
    console.log("Retrieved objects:", allObjs);

    // 确保关闭数据库连接
    db.close((err) => {
      if (err) {
        console.error("Error closing database", err);
      } else {
        console.log("Database closed.");
      }
    });

  } catch (error) {
    console.error("An error occurred:", error);
  }
})();

注意事项与最佳实践

  1. 错误处理: 在Promise中,务必在回调函数内部检查err参数。如果存在错误,应调用reject(err)来拒绝Promise,以便上层调用者能够捕获并处理错误。
  2. 类型断言: 在query.all()的回调中,rows通常被推断为any[]。在构建objs数组时,虽然我们知道row的结构,但为了更严格的类型检查,可以使用as Obj进行类型断言,例如objs.push(row as Obj),但这要求row的类型与Obj完全兼容。在上面的例子中,我们显式地创建了一个新对象,这提供了更好的控制和清晰度。
  3. async/await: 一旦你的函数返回Promise,你就可以在调用端使用async/await语法来进一步简化异步代码的编写和阅读,使其看起来更像同步代码。
    async function processData() {
      try {
        const objs = await GetAllObjs();
        console.log(objs);
      } catch (error) {
        console.error("Failed to get objects:", error);
      }
    }
    processData();
  4. 数据库连接管理: 确保在应用程序生命周期的适当时候打开和关闭数据库连接,以避免资源泄露。在Electron应用中,这通常在主进程启动时打开,在应用关闭前关闭。
  5. 参数化查询: 在插入或更新数据时,始终使用参数化查询(如INSERT INTO ... VALUES (?, ?))来防止SQL注入攻击。

总结

通过本教程,我们学习了如何在TypeScript中优雅地处理sqlite3的异步查询结果,并将其反序列化为强类型对象。关键在于:

  • 理解sqlite3.all()的异步回调机制。
  • 使用Promise封装异步操作,以获得更清晰、可维护的代码结构。
  • 利用for...of循环正确迭代数据库查询结果数组。
  • 将每一行数据安全地映射到预定义的TypeScript接口或类实例。

遵循这些实践,将大大提高你在Electron或Node.js项目中与SQLite数据库交互时的代码质量、类型安全性和开发效率。

热门AI工具

更多
Lovart
Lovart Hot

一款面向视觉设计创作的AI设计平台,可通过智能体和画布工作流辅助制作海报、Logo、网页、PPT及其他视觉内容。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

312

2026.02.13

TypeScript全栈项目架构与接口规范设计
TypeScript全栈项目架构与接口规范设计

本专题面向全栈开发者,系统讲解基于 TypeScript 构建前后端统一技术栈的工程化实践。内容涵盖项目分层设计、接口协议规范、类型共享机制、错误码体系设计、接口自动化生成与文档维护方案。通过完整项目示例,帮助开发者构建结构清晰、类型安全、易维护的现代全栈应用架构。

520

2026.02.25

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

371

2026.03.13

TypeScript 全栈开发进阶指南
TypeScript 全栈开发进阶指南

面向有 JavaScript 基础的开发者,深入讲解 TypeScript 的类型系统与全栈开发实践。

286

2026.06.03

TypeScript Node.js 全栈工程化与Monorepo架构实践
TypeScript Node.js 全栈工程化与Monorepo架构实践

本专题围绕 TypeScript 在 Node.js 全栈开发中的工程化实践展开,系统讲解 Monorepo 架构设计、包管理策略、模块复用机制以及服务端与前端统一类型系统的构建方法。通过真实项目案例,帮助开发者提升大型全栈项目的可维护性与协作效率。

658

2026.06.16

数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

4563

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

951

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

1189

2024.02.23

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

120

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

React 教程
React 教程

共58课时 | 12.3万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn