对模型类型的部分结构进行操作
在使用 Prisma Client 时,您的 Prisma Schema 中的每个模型都会被转换为一个专用的 TypeScript 类型。例如,假设您有以下 User
和 Post
模型:
model User {
id Int @id
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id
author User @relation(fields: [userId], references: [id])
title String
published Boolean @default(false)
userId Int
}
从这个 schema 生成的 Prisma Client 代码包含了 User
类型的此表示形式:
export type User = {
id: string
email: string
name: string | null
}
问题:使用生成模型类型的变体
描述
在某些场景中,您可能需要生成的 User
类型的变体。例如,当您有一个函数期望一个包含 posts
关系的 User
模型实例时。或者当您需要在应用程序代码中仅传递 User
模型的 email
和 name
字段的类型时。
解决方案
作为解决方案,您可以使用 Prisma Client 的辅助类型自定义生成的模型类型。
User
类型仅包含模型的标量字段,但不包含任何关系。这是因为在 Prisma Client 查询中,关系默认不包含。
然而,有时拥有一个包含关系的可用类型会很有用(即,您可以通过使用 include
的 API 调用获得的类型)。同样,另一个有用的场景是拥有一个只包含模型标量字段的子集的可用类型(即,您可以通过使用 select
的 API 调用获得的类型)。
实现这一点的一种方法是在应用程序代码中手动定义这些类型
// 1: Define a type that includes the relation to `Post`
type UserWithPosts = {
id: string
email: string
name: string | null
posts: Post[]
}
// 2: Define a type that only contains a subset of the scalar fields
type UserPersonalData = {
email: string
name: string | null
}
虽然这确实可行,但这种方法会在 Prisma schema 更改时增加维护负担,因为您需要手动维护这些类型。一个更简洁的解决方案是结合使用 validator
和 Prisma Client 在 Prisma
命名空间下生成并公开的 UserGetPayload
类型。
以下示例使用 Prisma.validator
创建了两个类型安全的对象,然后使用 Prisma.UserGetPayload
实用函数创建了一个可用于返回所有用户及其帖子的类型。
import { Prisma } from '@prisma/client'
// 1: Define a type that includes the relation to `Post`
const userWithPosts = Prisma.validator<Prisma.UserDefaultArgs>()({
include: { posts: true },
})
// 2: Define a type that only contains a subset of the scalar fields
const userPersonalData = Prisma.validator<Prisma.UserDefaultArgs>()({
select: { email: true, name: true },
})
// 3: This type will include a user and all their posts
type UserWithPosts = Prisma.UserGetPayload<typeof userWithPosts>
后一种方法的主要优势是:
- 更简洁的方法,因为它利用了 Prisma Client 生成的类型
- 在 schema 更改时减少了维护负担并提高了类型安全性
问题:获取函数的返回类型
描述
当对模型执行 select
或 include
操作并从函数返回这些变体时,很难访问到其返回类型,例如:
// Function definition that returns a partial structure
async function getUsersWithPosts() {
const users = await prisma.user.findMany({ include: { posts: true } })
return users
}
从上面的代码片段中提取代表“带有帖子的用户”的类型需要一些高级的 TypeScript 用法。
// Function definition that returns a partial structure
async function getUsersWithPosts() {
const users = await prisma.user.findMany({ include: { posts: true } })
return users
}
// Extract `UsersWithPosts` type with
type ThenArg<T> = T extends PromiseLike<infer U> ? U : T
type UsersWithPosts = ThenArg<ReturnType<typeof getUsersWithPosts>>
// run inside `async` function
const usersWithPosts: UsersWithPosts = await getUsersWithPosts()
解决方案
您可以优雅地使用原生的 TypeScript 实用类型 Awaited
和 ReturnType
来解决这个问题。
type UsersWithPosts = Awaited<ReturnType<typeof getUsersWithPosts>>