首页 / Prisma ORM 入门教程 / 类型安全:生成类型与 Prisma 类型工具

Prisma ORM 入门教程

类型安全:生成类型与 Prisma 类型工具

本教程共 54 篇 · 第 37 篇 · 更新于 2026-08-11 · 约 6 分钟阅读

类型安全生成类型UserGetPayloadsatisfiesPrismaPromiseUncheckedInput类型工具

本节目标:搞懂生成类型从哪来、长什么样,学会用它们约束自己的代码。

Prisma 的招牌是类型安全。Schema 是类型的唯一来源,改一处,全项目的类型跟着变。这一章把生成类型体系讲透:有哪些类型、怎么用、怎么派生自己的类型。

生成类型:Schema 的投影

每个模型都会生成一组类型,挂在 Prisma 命名空间下,从自定义 output 路径导入:

import { Prisma } from "./generated/prisma/client";

以 User 模型为例,UserSelect 描述 select 能选哪些字段:

const userEmail: Prisma.UserSelect = {
  email: true,
};

写错字段名、给关系字段传了 boolean 之外的选项,编译期就报错。同一族类型还有 UserIncludeUserWhereInputUserCreateInputUserUpdateInput,分别对应 include、where、create、update 的参数形状。加上 UserFindManyArgsUserDefaultArgs 这类按操作细分的类型,整套参数类型齐了。

输入类型特别适合放在接口层。API 的请求体类型直接用 Prisma.UserCreateInput,与数据库 Schema 严格对齐,少一类 bug:

async function createUser(data: Prisma.UserCreateInput) {
  return prisma.user.create({ data });
}

多数时候类型由 TypeScript 自动推断,不用手写标注。需要标注的场合,用这些生成类型最稳。别忘了生成类型是编译期产物,它保证的是「类型正确」,不保证「数据合法」;运行时校验(比如 Zod)仍然需要。

类型系统原理:标量到 TS 的映射

每个标量类型都有固定的 TS 映射:

Prisma 类型TS 类型
Stringstring
Int / Floatnumber
Booleanboolean
DateTimeDate
BigIntbigint
DecimalPrisma.Decimal
BytesUint8Array
JsonJsonValue

可选字段(?)映射为 string | null,列表字段([])映射为数组。DateTime 在代码里是 Date 对象,不要传字符串,否则运行时报错;Decimal 是 Prisma.Decimal 实例,专门解决浮点精度;Bytes 是二进制数组。原生类型属性 @db.* 只影响数据库里的列类型,不影响 TS 类型。比如 DateTime @db.Date 在数据库里是 date 列,TS 里仍然是 Date。内省(Introspection)时,非默认的数据库类型会自动补上 @db.* 标注。

部分结构类型:UserGetPayload + satisfies

查询结果常常只带部分字段或附加关系。手动定义这些类型,Schema 一改就得跟着改,维护成本高。正确做法是用 Prisma.UserGetPayload 配合 TypeScript 的 satisfies 从查询参数推导:

const userWithPosts = {
  include: { posts: true },
} satisfies Prisma.UserDefaultArgs;

type UserWithPosts = Prisma.UserGetPayload<typeof userWithPosts>;

satisfies 先校验对象合法,再让类型推导拿到精确的字面量。UserDefaultArgs 换成 UserFindManyArgsUserFindFirstArgs 同理。派生出的类型永远与 Schema 同步,Schema 加了字段,这里自动带上。

函数返回类型也能提取。先写函数,再用两个 TS 内置工具:

async function getUsersWithPosts() {
  return prisma.user.findMany({ include: { posts: true } });
}

type UsersWithPosts = Awaited<ReturnType<typeof getUsersWithPosts>>;

ReturnType 拿到函数返回的 Promise 类型,Awaited 剥掉 Promise 外壳。改查询参数,类型自动跟着变,不用维护第二份。

PrismaPromise:可等待的查询

Prisma Client 的查询方法返回的不是普通 Promise,而是 PrismaPromise<T>。它是 thenable,可以直接 await,这点和 Promise 没有区别。额外能力是能被 $transaction 批量执行:

const queries: PrismaPromise<any>[] = [
  prisma.user.update({ where: { id: 1 }, data: { name: "Alice" } }),
  prisma.post.create({ data: { title: "Hello", authorId: 1 } }),
];

await prisma.$transaction(queries); // 全部成功或全部回滚
Note

$transaction 只接受 PrismaPromise 数组,所以查询不能先 await 再传,要传未等待的查询对象。

UncheckedInput:安全版与直写版

create、update、upsert 的输入类型有两套。以 Post 为例:

// 安全版:通过关系字段连接作者
await prisma.post.create({
  data: {
    title: "Hello",
    author: { connect: { id: 1 } },
  },
});

// Unchecked 版:直接写外键
await prisma.post.create({
  data: {
    title: "Hello",
    authorId: 1,
  },
});

authorId 这种关系标量字段只存在于 PostUncheckedCreateInput 里。Unchecked 名字的由来:直接写外键绕过了关系层,类似「不检查」的快捷通道。它省事,但少了关系校验,报错信息也更含糊。安全版虽然嵌套更深,写出来的意图更清楚。官方建议优先用安全版输入类型,需要 connectOrCreate 之类的原子语义时它更好用。

四个类型工具

客户端扩展和泛型代码里,四个类型工具很常用:

工具作用
Exact<Input, Shape>严格校验输入与目标形状一致
Args<Type, Operation>取某模型某操作的参数类型
Result<Type, Args, Operation>取某模型某操作的返回类型
Payload<Type, Operation>取结果的完整结构(标量与关系)

Exact 用于收紧泛型参数,杜绝多余属性;Result 和 Args 搭配,能精确表达「输入这样、输出那样」的函数签名;Payload 用于在类型层面判断哪些键是标量、哪些是关系。它们都动态适配任何模型,是写共享扩展的利器。

最实用的是 Args。比如让函数的参数严格匹配 post.create:

type PostCreateBody = Prisma.Args<typeof prisma.post, "create">["data"];

async function addPost(postBody: PostCreateBody) {
  return prisma.post.create({ data: postBody });
}

调用方传错字段,编译期立刻报错。第 36 章的 exists 扩展就是靠 Prisma.Args<T, "findFirst">["where"] 复用了 findFirst 的过滤条件类型,写扩展时这是标配。

用 Prisma.validator 校验复杂条件

动态拼 where 时,类型容易在中间步骤丢失。Prisma.validator 可以锚定类型,让中间变量也享受自动补全:

const whereClause = Prisma.validator<Prisma.UserWhereInput>()({
  email: { contains: "@example.com" },
});

const users = await prisma.user.findMany({ where: whereClause });

后面的尖括号是必需的,它让 validator 的泛型参数指向目标类型。拼复杂过滤条件、把 where 拆到多个函数里时,这个工具很实用。

参考来源

  • Prisma 官方文档:Type safety Overview
  • Prisma 官方文档:How to use Prisma ORM’s type system
  • Prisma 官方文档:Operating against partial structures of your model types
  • Prisma 官方文档:Client extensions Type utilities