使用Prisma select时如何优雅处理Typescript类型定义?
Prisma 带select/include查询的类型安全优雅方案
不用手动写类型的方案有好几种,都是利用TypeScript和Prisma的内置能力,完全避免繁琐的手动维护:
方案1:用Prisma内置的Payload类型提取器
Prisma会根据你的查询配置自动生成精确的返回类型,通过Prisma.ModelNameGetPayload就能直接拿到,步骤如下:
- 先把查询的select/include配置单独抽出来(方便复用和类型推导):
const basicUserQuery = { select: { id: true, name: true, } } satisfies Prisma.UserFindUniqueArgs;
- 用
Prisma.UserGetPayload提取对应的返回类型:
import { type Prisma, type User } from "@prisma/client"; import { useState } from "react"; // 自动推导查询返回的类型 type BasicUser = Prisma.UserGetPayload<typeof basicUserQuery>; // 直接用这个类型定义状态 const [user, setUser] = useState<BasicUser | null>(null); // 实际查询时复用配置 const fetchUser = async (userId: string) => { const result = await prisma.user.findUnique({ where: { id: userId }, ...basicUserQuery, }); setUser(result); };
不管你后续怎么修改select/include的字段,类型都会自动同步,完全不用手动改类型定义。
方案2:用TypeScript的Pick工具类型快速生成子类型
如果只是简单挑选原模型的几个字段,直接用TypeScript内置的Pick工具,从原User类型中挑出需要的字段即可:
import { type User } from "@prisma/client"; import { useState } from "react"; // 从User类型中挑选id和name字段 type BasicUser = Pick<User, "id" | "name">; const [user, setUser] = useState<BasicUser | null>(null);
这种方式比手动写类型简洁得多,而且原User模型的字段类型变更时,这里的子类型也会自动更新,不会出现类型不一致的问题。
方案3:封装查询函数后提取返回类型
如果某个查询逻辑在多个组件里复用,可以直接封装成函数,然后用ReturnType提取它的返回类型:
import { prisma } from "@prisma/client"; import { useState } from "react"; // 封装查询逻辑 const fetchBasicUser = async (userId: string) => { return await prisma.user.findUnique({ where: { id: userId }, select: { id: true, name: true, }, }); }; // 提取函数的返回类型(自动处理Promise包装) type BasicUser = ReturnType<typeof fetchBasicUser> extends Promise<infer T> ? T : never; const [user, setUser] = useState<BasicUser | null>(null); // 调用封装好的函数 const loadUser = async () => { const data = await fetchBasicUser("user-id-123"); setUser(data); };
这种方式的好处是类型完全由查询逻辑驱动,不需要额外维护类型定义,适合复杂查询的复用场景。
内容的提问来源于stack exchange,提问作者Malun
相关产品推荐
相关产品推荐

