Next.js中序列化含Date对象的变量且不丢失类型的最佳实践
解决Next.js + Prisma + Tanstack Table中Date类型序列化的类型丢失问题
下面是几种无需手动逐个转换、且能复用PostData类型的实用方案:
方案1:自动序列化/反序列化工具函数+类型安全
写通用工具函数批量处理Date类型转换,同时通过类型定义保证前后端类型一致,不用手动逐个字段修改:
// utils/dateSerializer.ts import type { PostData } from '../pages/columns'; // 序列化:将PostData中的Date转为ISO字符串 export const serializePosts = (posts: PostData[]): Omit<PostData, 'lastModified'>[] => { return posts.map(post => ({ ...post, lastModified: post.lastModified.toISOString() })); }; // 反序列化:将字符串转回Date,恢复PostData类型 export const deserializePosts = (posts: Omit<PostData, 'lastModified'>[]): PostData[] => { return posts.map(post => ({ ...post, lastModified: new Date(post.lastModified) })); };
在页面中使用:
// pages/index.tsx import { serializePosts, deserializePosts } from '../utils/dateSerializer'; import type { PostData } from './columns'; export const getStaticProps: GetStaticProps = async () => { const result = await prisma.post.findMany({ select: { id: true, title: true, lastModified: true }, }); const serializedData = serializePosts(result as PostData[]); return { props: { data: serializedData } }; }; const Home = ({ data }: { data: ReturnType<typeof serializePosts> }) => { // 反序列化后得到标准PostData类型 const posts = deserializePosts(data); return <TanStackTable columns={columns} data={posts} />; };
方案2:创建客户端专用类型,避免反序列化
如果不想在客户端做类型转换,直接定义一个兼容前端序列化后数据的类型,复用PostData的结构:
// pages/columns.tsx export type PostData = Prisma.PostGetPayload<typeof post>; // 客户端专用类型:将lastModified改为字符串类型 export type ClientPostData = Omit<PostData, 'lastModified'> & { lastModified: string }; // 调整columnHelper为ClientPostData类型 const columnHelper = createColumnHelper<ClientPostData>(); export const columns = [ columnHelper.accessor("title", { header: "Title" }), columnHelper.accessor("lastModified", { header: "Last Modified", // 在单元格渲染时直接转换为Date展示 cell: ({ value }) => new Date(value).toLocaleString() }) ];
在页面中使用:
// pages/index.tsx import type { ClientPostData } from './columns'; export const getStaticProps: GetStaticProps = async () => { const result = await prisma.post.findMany({ select: { id: true, title: true, lastModified: true }, }); const data: ClientPostData[] = result.map(row => ({ ...row, lastModified: row.lastModified.toISOString() })); return { props: { data } }; }; const Home = ({ data }: { data: ClientPostData[] }) => { // 直接将data传给表格,无需额外转换 return <TanStackTable columns={columns} data={data} />; };
方案3:Prisma中间件自动序列化Date
通过Prisma中间件,在查询完成后自动将Date字段转为字符串,减少页面层的重复代码:
// prisma/middleware.ts import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); // 注册中间件,自动处理Post模型的Date字段 prisma.$use(async (params, next) => { const result = await next(params); if (params.action === 'findMany' && params.model === 'Post') { return (result as any[]).map(item => ({ ...item, lastModified: item.lastModified.toISOString() })); } return result; }); export default prisma;
之后在页面中直接使用这个带中间件的Prisma客户端,搭配方案2的ClientPostData类型即可,不用手动map转换。
方案4:类型断言快速兼容
如果只是需要类型提示,可直接通过类型断言在客户端恢复PostData类型,同时保证运行时安全:
// pages/index.tsx import type { PostData } from './columns'; export const getStaticProps: GetStaticProps = async () => { const result = await prisma.post.findMany({ select: { id: true, title: true, lastModified: true }, }); const data = result.map(row => ({ ...row, lastModified: row.lastModified.toISOString() })); return { props: { data } }; }; const Home = ({ data }: { data: Omit<PostData, 'lastModified'>[] }) => { // 类型断言恢复PostData类型,同时运行时转换为Date const posts = data.map(post => ({ ...post, lastModified: new Date(post.lastModified) })) as PostData[]; return <TanStackTable columns={columns} data={posts} />; };
内容的提问来源于stack exchange,提问作者yumba
相关产品推荐
相关产品推荐

