You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 22:05:16