如何在Prisma中为JSON字段处理Date类型的类型定义?
Prisma + PlanetScale(MySQL)JSON字段处理Date类型的解决方案
问题根源
Prisma的InputJsonValue类型不兼容原生Date对象,同时MySQL的JSON类型仅支持字符串、数字、布尔、数组、对象、null这些基础类型,Date对象无法直接被序列化存储,所以会触发TS类型错误;直接用JSON.stringify()会导致Prisma二次序列化,产生双重转义的JSON字符串。
解决方案1:手动转换Date字段(简单直接)
存入数据库前将Date转为ISO字符串,取出时再转回Date对象,避免类型错误和双重转义。
// 存入前处理Date字段 const processedTp = { ...tp, trained_at: tp.trained_at.toISOString() }; const newPrompt = await prisma.tunePrompt.create({ data: { apiData: processedTp } }); // 取出后还原Date字段 const savedPrompt = await prisma.tunePrompt.findUnique({ where: { id: newPrompt.id } }); if (savedPrompt?.apiData?.trained_at) { savedPrompt.apiData.trained_at = new Date(savedPrompt.apiData.trained_at); }
- 优点:无额外依赖,实现简单
- 缺点:读写操作需手动处理,存在代码重复
解决方案2:Prisma中间件自动处理
通过Prisma中间件全局拦截读写操作,自动完成Date的序列化/反序列化,无需重复编写转换逻辑。
// 序列化:Date转ISO字符串 function serializeDates(obj: any): any { if (obj instanceof Date) { return obj.toISOString(); } else if (Array.isArray(obj)) { return obj.map(item => serializeDates(item)); } else if (typeof obj === 'object' && obj !== null) { return Object.fromEntries( Object.entries(obj).map(([key, value]) => [key, serializeDates(value)]) ); } return obj; } // 反序列化:ISO字符串转Date function deserializeDates(obj: any): any { if (typeof obj === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(obj)) { return new Date(obj); } else if (Array.isArray(obj)) { return obj.map(item => deserializeDates(item)); } else if (typeof obj === 'object' && obj !== null) { return Object.fromEntries( Object.entries(obj).map(([key, value]) => [key, deserializeDates(value)]) ); } return obj; } // 注册Prisma中间件 prisma.$use(async (params, next) => { // 处理写入操作(create/update/upsert等) if (['create', 'update', 'upsert'].includes(params.action)) { if (params.args.data.apiData) { params.args.data.apiData = serializeDates(params.args.data.apiData); } } // 处理读取操作(find/findMany等) const result = await next(params); if (result && typeof result === 'object') { if (Array.isArray(result)) { return result.map(item => item.apiData ? { ...item, apiData: deserializeDates(item.apiData) } : item); } else { return result.apiData ? { ...result, apiData: deserializeDates(result.apiData) } : result; } } return result; });
- 优点:全局自动处理,无重复代码
- 缺点:需维护中间件逻辑,复杂嵌套结构可能需要调整日期匹配正则
解决方案3:扩展InputJsonValue类型(仅解决TS报错,需配合运行时处理)
如果只是想消除TS编译错误,可以扩展Prisma的InputJsonValue类型,让它支持Date,但运行时仍需将Date转为字符串(否则MySQL无法存储)。
在项目根目录创建prisma/types.d.ts文件:
import { InputJsonValue as OriginalInputJsonValue } from '@prisma/client'; type InputJsonValue = OriginalInputJsonValue | Date; declare module '@prisma/client' { interface InputJsonValue extends OriginalInputJsonValue { [key: string]: InputJsonValue | Date | string | number | boolean | null; } }
注意:此方案仅解决类型提示问题,必须配合方案1或2的运行时转换,否则写入数据库时会失败。
为什么JSON.stringify会导致双重转义?
Prisma会自动将传入的JSON对象序列化为JSON字符串存入MySQL,若提前调用JSON.stringify(tp),Prisma会再次对这个字符串进行序列化,最终存储的是嵌套的JSON字符串(例如"{\"trained_at\":\"2024-01-01T00:00:00.000Z\"}"),而非预期的JSON对象。
内容的提问来源于stack exchange,提问作者dcsan
相关产品推荐
相关产品推荐

