30

When i generate my prisma client with prisma generate, i got an index.d.ts with all types from my database. But the probleme is that all type are "single" and there is no relations. When i query something like

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

prisma type the response dynamically with the right type

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

So everything work well and I have the auto completion, except if I want to pass this variable to another method, I would like to type the parameters, so I have to create my own type used as method parameter.

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

But I'm asking if there is a way to get the "full" type from prisma to avoid me to create all "full" types with optionals sub-elements like I did for the user example. Or maybe i'm doing wrong and there is another way to do?

dufaux
  • 681
  • 2
  • 8
  • 18

8 Answers8

51

An alternate method (my preferrence) to get the "full" type, or the model with all of its possible relations, is to use the GetPayload version of your model type. The type is a generic that can receive the same object you pass to your query.

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,
  }
});

As your query grows more complex, you may want to abstract it and ensure that it is strongly typed.

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,
});

joematune
  • 1,087
  • 10
  • 16
  • 3
    Upvoting because your solution does not need to create a function just to retrieve a type :) Thanks! – Gin Quin May 09 '22 at 11:58
14

You can use the Prisma Validator API along with some typescript features to generate types for your query.

For the findMany example you mentioned

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>[]

If you simply want to automatically infer the return type of a prisma query wrapped in a function, you can use PromiseReturnType.

For example:

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>

You can read more about this in the Operating against partial structures of your model types concept guide in the Prisma docs.

Tasin Ishmam
  • 5,670
  • 1
  • 23
  • 28
8

The best way is to use generic typing like this :

import { Prisma } from '@prisma/client'

type UserWithMessages = Prisma.UserGetPayload<{
  include: {
    Message: {
      include: {
        MessageParam: true;
      };
    };
  };
}>;
Cam CHN
  • 3,377
  • 1
  • 19
  • 12
7

Full type of Model with all relations.

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

You could also create maps for convenience.

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]

Usage

const user: FullModel<'User'>

Update

Here is the workaround solution to make it using Model type as generic

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> = {}
shtse8
  • 1,092
  • 12
  • 20
1

Most easily you can define the corresponding type with typeof:

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

Or if you decide to wrap your query inside a function, you may let TypeScript infer the return type for you:

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

and then extract the function's return type:

type prismaUser = ReturnType<typeof queryUserWithCar> extends Promise<infer T> ? T : never
daylily
  • 876
  • 5
  • 11
0

To clarify what I use this a year now since there was not a lot of answer yet:

I am still using my "own" type. I have a big file with all types where all properties are optional. It is not the best way because depending of the query, u have no access to all sub objects, but it is really pleasant to work with for the new devs.

It seem a bit heavy to develop but just need to add the new liaison when we change the database.

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

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

type prismaHouse = house

On this example we only work with prismaUser, prismaCar and prismaHouse.

We think to change about new suggestions with generic type here when we gonna have time

dufaux
  • 681
  • 2
  • 8
  • 18
0

Maybe this will help, I had this query

  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,
        },
      },
    },
  });

So I used RecursivePartial

export type RecursivePartial<T> = {
  [P in keyof T]?: RecursivePartial<T[P]>;
};
type UserWithPatientAndOrderHistory = RecursivePartial<
  Prisma.UserGetPayload<{
    include: {
      patientData: true;
      OrderHistory: true;
    };
  }>
>;
Aladdin Mhemed
  • 3,817
  • 9
  • 41
  • 56
0

The above example gives a really good solution, I just want to add how can you select specific attributes of a loaded entity.

In my example I have a todo entity and this entity has a author field on it. I want to load only name and id of the author entity

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;
}>;