关于在Electron+TypeScript应用中使用Kysely实现类型安全数据库查询服务的咨询
太懂你这种用原生SQL写Electron+TS项目的糟心经历了——关联表查询的时候,字段名写错、类型不匹配全要等运行时才发现,之前用Yup做验证也只能管输入,管不了SQL查询里的类型问题。Kysely简直就是为这种场景量身定做的,它能把你的数据库结构直接映射成TS类型,让整个查询过程全程享受类型提示和校验,完全不用靠脑子记字段!
下面给你一步步拆解怎么落地:
第一步:先把数据库结构转成TS类型(核心基础)
你要先给每张表定义对应的TS接口,把字段名和类型对应上,再组合成整个数据库的类型,最后传给Kysely的实例。这样Kysely就自动知道你每张表有哪些字段了。举个简单的例子:import { Kysely, SqliteDialect } from 'kysely'; import sqlite3 from 'sqlite3'; // 定义单表结构,完全对应你的数据库字段 interface Users { id: number; username: string; email: string; created_at: Date; } interface Posts { id: number; user_id: number; title: string; content: string; published: boolean; } // 组合成整个数据库的类型映射 interface Database { users: Users; posts: Posts; } // 初始化Kysely实例,适配Electron常用的SQLite数据库 const db = new Kysely<Database>({ dialect: new SqliteDialect({ database: new sqlite3.Database('./app.db'), // 注意Electron中数据库文件的正确路径 }), });第二步:类型安全的查询(包括关联表)
有了上面的类型定义,不管是单表查询还是关联表查询,Kysely都会自动推导可用字段,一旦你写错字段名或者用了不属于当前查询的字段,TS会直接在编译阶段报错,根本轮不到运行时踩坑。比如:// 单表查询:自动提示users表的所有合法字段 async function getUserById(userId: number) { return await db .selectFrom('users') .select(['id', 'username', 'email']) // 要是写个不存在的字段,比如nickname,TS直接红 .where('id', '=', userId) .executeTakeFirst(); } // 关联表查询:join之后的字段也能被自动识别 async function getUserWithPosts(userId: number) { return await db .selectFrom('users') .innerJoin('posts', 'posts.user_id', 'users.id') .select([ 'users.id as user_id', 'users.username', 'posts.title as post_title', 'posts.content' ]) .where('users.id', '=', userId) .execute(); }第三步:和Yup验证无缝结合,复用类型
你之前用Yup做输入验证,现在完全可以复用Kysely的表类型,不用重复定义字段结构,避免类型不一致的问题:import * as yup from 'yup'; // 复用Users类型,只排除自动生成的字段(比如id、created_at) const userCreateSchema = yup.object().shape({ username: yup.string().required(), email: yup.string().email().required(), } as yup.ObjectSchema<Omit<Users, 'id' | 'created_at'>>); // 新增用户时先验证,再插入,类型完全匹配 async function createUser(userData: Omit<Users, 'id' | 'created_at'>) { // 先做输入验证 await userCreateSchema.validate(userData); // 插入时Kysely会校验字段是否合法 return await db .insertInto('users') .values(userData) .executeTakeFirstOrThrow(); }Electron环境的额外注意点
别忘了Electron有主进程和渲染进程的区分,数据库操作一定要放在主进程里做,渲染进程通过IPC调用主进程的数据库服务,这样既安全又能避免跨进程的数据库连接问题。比如主进程暴露IPC方法,渲染进程调用就行,类型也能完美传递。
这样一套流程走下来,你既能保留SQL的灵活性,又能享受到TS的全程类型安全,关联表查询的字段再也不会写错,还能复用类型给Yup做验证,完美解决你之前遇到的痛点!
内容来源于stack exchange

