如何将Kysley查询结果转换为自动生成的类型?
Kysley插入数据后返回强类型实体的问题与优化方案
问题描述
我通过kysley-codegen为PostgreSQL数据库生成了类型,其中QuestionReview接口定义如下:
export interface QuestionReview { id: Generated<number>; // 存在类型不匹配问题 idQuestion: number; idUserReviewed: number; tsCreated: Timestamp; // 存在类型不匹配问题 tsModified: Timestamp; // 存在类型不匹配问题 userModified: string; }
在服务类中,我向该表插入数据后,希望返回Promise<QuestionReview>类型的新插入实体,实现代码如下:
const rv = await this.db .insertInto('questionReview') .values({ idQuestion: idQuestion, idUserReviewed: idAppUser, } as Insertable<QuestionReview>) .returningAll() .executeTakeFirstOrThrow();
实际执行后,返回的rv对象的JavaScript运行时类型为:
{ id: number, ... tsCreated: Date, tsModified: Date }
由于TypeScript无法自动完成以下类型转换,我无法直接返回该对象:
number转换为Generated<number>Date转换为Timestamp
目前我只能通过强制类型转换的方式返回:
return { ...rv, id: rv.id as unknown as Generated<number>, tsCreated: rv.tsCreated as unknown as Timestamp, tsModified: rv.tsModified as unknown as Timestamp, };
请问我的做法是否有误?有没有更优的方法返回强类型的插入实体?
解决方案
你的做法本身没有错误,但可以通过以下几种方式简化或优化:
1. 直接对整个对象做类型断言
Kysley的Generated<T>只是编译时标记字段为数据库自动生成,运行时实际值就是T类型;Timestamp在运行时本质上就是Date。因此你不需要逐个字段转换,直接对整个对象做断言即可:
return rv as unknown as QuestionReview;
这种方式更简洁,且和逐个字段转换的效果完全一致——因为运行时类型完全匹配,只是编译时需要对齐类型定义。
2. 定义专用的运行时类型(提升可读性)
如果觉得直接断言不够清晰,可以先定义一个和数据库实际返回结构一致的类型,再转换为目标类型:
// 定义和运行时返回结构匹配的类型 type QuestionReviewRuntime = Omit<QuestionReview, 'id' | 'tsCreated' | 'tsModified'> & { id: number; tsCreated: Date; tsModified: Date; }; // 插入时先断言为运行时类型 const rv = await this.db .insertInto('questionReview') .values({ idQuestion, idUserReviewed } as Insertable<QuestionReview>) .returningAll() .executeTakeFirstOrThrow() as QuestionReviewRuntime; // 转换为目标接口类型 return rv as unknown as QuestionReview;
这种方式能更清晰地区分编译时类型定义和运行时实际类型,同时保持代码简洁。
3. 调整kysley-codegen生成配置
检查kysley-codegen的配置项,看是否可以调整类型映射规则:
- 对于
Timestamp类型,可尝试配置将其直接映射为Date(例如设置timestampType: 'Date',具体看代码生成工具的文档),这样生成的QuestionReview接口中时间字段的类型就是Date,避免类型转换。 - 对于
Generated<T>,它是Kysley用于标记插入时无需提供字段的编译时类型,运行时不存在对应的实体类型,因此无论如何都需要编译时的类型断言来对齐。
内容的提问来源于stack exchange,提问作者igorludi
相关产品推荐
相关产品推荐

