获取 Prisma Client 的完整类型。

32

使用prisma generate生成Prisma客户端后,会得到一个包含数据库所有类型的index.d.ts。 但问题是所有类型都是“单个”类型,没有关系类型。 当我查询类似于以下内容时:

prisma.users.findMany({
            [... ]
            include: {
                cars: {
                  [...]
                }}});

Prisma动态地使用正确的类型对响应进行类型定义。

users & {
        cars: cars & {
           [...]
        };
    }

一切都运行良好,自动完成也可以使用。但是如果我想将这个变量传递给另一个方法,并且希望使用参数类型,则需要创建自己的类型作为方法参数。

type prismaUsers = users & {
        cars?: cars & {
           [...]
        };
    }

但我想问是否有一种方法可以从 Prisma 中获取“完整”的类型,以避免像我为用户示例所做的那样创建所有带有可选子元素的“完整”类型。或者也许我做错了,有另一种方法可以做到这一点?

8个回答

56

获取“完整”类型或具有所有可能关系的模型的另一种方法(我更喜欢的方法)是使用您的模型类型的 GetPayload 版本。该类型是一个泛型,可以接收您传递给查询的相同对象。

import { PrismaClient, Prisma } from "@prisma/client";

const prisma = new PrismaClient();

type UserWithCars = Prisma.UserGetPayload<{
  include: {
    cars: true;
  }
}>

const usersWithCars = await prisma.user.findMany({
  include: {
    cars: true,
  }
});

随着您的查询变得更加复杂,您可能希望将其抽象化并确保其具有强类型。

import { Prisma, PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

const userInclude = Prisma.validator<Prisma.UserInclude>()({
  cars: true,
});

type UserWithCars = Prisma.UserGetPayload<{
  include: typeof userInclude;
}>;

const usersWithCars = await prisma.user.findMany({
  include: userInclude,
});


3
点赞,因为您的解决方案不需要创建一个函数来检索类型 :) 谢谢! - Gin Quin

15
你可以使用 Prisma Validator API和一些typescript功能来为查询生成类型。对于你提到的findMany示例。
import { Prisma } from '@prisma/client'

// 1. Define a User type that includes the "cars" relation. 
const userWithCars = Prisma.validator<Prisma.UserArgs>()({
    include: { cars: true },
})

// 2: This type will include many users and all their cars
type UserWithCars = Prisma.UserGetPayload<typeof userWithCars>[]

如果您只想自动推断包装在函数中的 Prisma 查询的返回类型,您可以使用 PromiseReturnType

例如:

import { Prisma } from '@prisma/client'

async function getUsersWithCars() {
  const users = await prisma.user.findMany({ include: { cars: true } });
  return users;
}

type UsersWithCars = Prisma.PromiseReturnType<typeof getUsersWithCars>

您可以在Prisma文档中的“操作部分模型类型的高级类型安全性”概念指南中了解更多信息。

8

最好的方法是使用通用类型,如下:

import { Prisma } from '@prisma/client'

type UserWithMessages = Prisma.UserGetPayload<{
  include: {
    Message: {
      include: {
        MessageParam: true;
      };
    };
  };
}>;

7

包含所有关系的完整模型类型。

type UserFullType = Prisma.UserGetPayload<{ select: { [K in keyof Required<Prisma.UserSelect>]: true } }>

您也可以创建地图以提高使用方便性。

interface SelectMap {
  User: Prisma.UserSelect
  Post: Prisma.PostSelect
}

interface PayloadMap<S extends (string | number | symbol)> {
  User: Prisma.UserGetPayload<{ [K in S]: true }>
  Post: Prisma.PostGetPayload<{ [K in S]: true }>
}

type FullModel<M extends keyof SelectMap, S = Required<SelectMap[M]>> = PayloadMap<keyof S>[M]

使用方法

const user: FullModel<'User'>

更新

这是一种解决方法,使得使用Model类型作为泛型成为可能。

type ValueOf<T> = T[keyof T]
type PickByValue<T, V extends T[keyof T]> = { [ K in Exclude<keyof T, ValueOf<{ [ P in keyof T ]: T[P] extends V ? never : P }>> ]: T[K] }
type KeyOfValue<T, V extends T[keyof T]> = keyof PickByValue<T, V>
type PickValueByKey<T, K> = K extends keyof T ? T[K] : never

interface ModelMap {
  Article: Article
  User: User
}
interface SelectMap {
  Article: Prisma.ArticleSelect
  User: Prisma.UserSelect
}
interface PayloadMap<S extends (string | number | symbol)> {
  Article: Prisma.ArticleGetPayload<{ select: { [K in S]: true } }>
  User: Prisma.UserGetPayload<{ select: { [K in S]: true } }>
}
type FullModelType<M extends ValueOf<ModelMap>, N = KeyOfValue<ModelMap, M>, S = Required<PickValueByKey<SelectMap, N>>> = PickValueByKey<PayloadMap<keyof S>, N>
const article: FullModelType<Article> = {}

1

最简单的方法是使用typeof来定义相应的类型:

myQuery = await prisma.users.findMany({
            [... ]
            include: {
                cars: {
                  [...]
                }}});
type prismaUsers = typeof myQuery

或者,如果您决定将查询包装在函数中,您可以让TypeScript为您推断返回类型:

function queryUserWithCar(...) {
  return prisma.users.findMany({
            [... ]
            include: {
                cars: {
                  [...]
                }}});
}

然后提取函数的返回类型:

type prismaUser = ReturnType<typeof queryUserWithCar> extends Promise<infer T> ? T : never

1
上面的例子提供了一个非常好的解决方案,我只想补充一下如何选择加载实体的特定属性。
在我的例子中,我有一个待办事项实体,这个实体上有一个作者字段。我只想加载作者实体的姓名和ID。
import { type Todo, Prisma } from "@prisma/client";

const todoInclude = Prisma.validator<Prisma.TodoInclude>()({
  author: {
    select: {
      name: true,
      id: true,
    },
  },
});

type TodoWithUser = Prisma.TodoGetPayload<{
  include: typeof todoInclude;
}>;

0

为了澄清我现在使用这个已经一年了,因为当时还没有很多答案:

我仍然使用我的“自己”的类型。我有一个包含所有类型的大文件,其中所有属性都是可选的。这不是最好的方法,因为根据查询,您无法访问所有子对象,但对于新开发人员来说,这真的很方便。

它似乎有点繁重,但只需要在更改数据库时添加新的联接即可。

type prismaUser = users & {
        car?: prismaCar[];
        house?: prismaHouse;
    }

type prismaCar = car & {
        user?: prismaUser;
    }

type prismaHouse = house

在这个例子中,我们只使用prismaUser、prismaCar和prismaHouse。

我们计划在有时间的时候,在这里使用通用类型来进行新建议的更改。


0
也许这会有帮助, 我有这个查询
  await this.prisma.user.findMany({
    where: {
      patientData: {},
    },
    select: {
      id: true,
      firstName: true,
      lastName: true,
      email: true,
      patientData: {
        select: {
          credits: true,
        },
      },
      OrderHistory: {
        select: {
          name: true,
          date: true,
        },
      },
    },
  });

所以我使用了RecursivePartial

export type RecursivePartial<T> = {
  [P in keyof T]?: RecursivePartial<T[P]>;
};
type UserWithPatientAndOrderHistory = RecursivePartial<
  Prisma.UserGetPayload<{
    include: {
      patientData: true;
      OrderHistory: true;
    };
  }>
>;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接