prisma: Specifying the "select" param in a query using a variable with a defined type breaks types
Bug description
After upgrading Prisma from 2.2 to 2.5, I’m now running into an issue where, when I try to provide the select parameter using a typed variable rather than inline code in a query, the result of the query no longer has any type information associated with it.
For example, I have a User type that has a number of properties, like id, createdAt, etc.
If I attempt to do a query with the select being defined inline, types are present on the result as expected:

If I now define my selection property using a typed variable, however (with the type corresponding to to the type expected by the Prisma for the query), and the pass that in instead, the result no longer has any properties, being typed as an empty object instead:

Oddly, if I remove the typing on the select variable, type information returns to the result (but I no longer have type suggestions when composing the select variable)

How to reproduce
Take any query, extract the select property into a typed variable.
Expected behavior
I expect the result of the query to have type information that corresponds to what the select variable is set to.
Prisma information
This appears to apply to any and all schemas, types, and queries.
Environment & setup
- OS: Windows
- Database: PostgreSQL
- Node.js version: 10.16
- Prisma version: 2.5.1
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 5
- Comments: 31 (14 by maintainers)
Commits related to this issue
- feat: add validator utility type. Closes #3372 — committed to prisma/prisma by timsuchanek 3 years ago
Thanks for all the comments. We are talking about two distinct problems here.
1. Problem: How do I separately construct the
selectorincludeargument and not just inline?2. Problem: How do I create a wrapper function around any Prisma query, like
findManythat is typesafe?These problems require separate solutions. In the following, I’ll show how they’re solvable today and also mention a potential utility type that we might want to generate in the client to save you some typing.
1. Problem: Dynamically construct
selectorincludeThis is, what @artemzakharov and @vh13294 requested. This can be achieved with a little helper function, as Harshit already noted. Here called
makeUserSelect:The same for
include:We can even do this for the whole args, here an example for
findMany:With our current knowledge, we need to create one function per type we want to “make”. We will later investigate if we could simplify that.
2. Problem: How do I create a wrapper function around any Prisma query, like
findManythat is typesafe?This is, what @MichalLytek and @Sytten talk about. This can be done without any helper type. We can make this happen both for
selectandinclude, but not the whole args:Just passing in
select, as requested by @MichalLytekThe same for
include:We can’t do this for the whole args here:
But that also hasn’t been asked 🙂
Does this solve your problem? Please let us know 🙏
Next steps for us at Prisma:
make...types we want to expose@pantharshit00 I still have a problem with the
selectgeneric argument. It works well when I define it as required:However, I would like to maintain the optional select behavior, so without providing that argument it returns full entity:
The problem is that when I add
?, it now always return{}as the return type of the data. The mentioned workaround{ select: S } & Omit<Prisma.UserFindFirstArgs, 'select' | 'include'>however makes this function always return fullUserentity, not the subset, ignoring the providedselectargument.How can I type this? Maybe we can use conditional type and check if it’s defined, then have return type based on
Prisma.UserGetPayload? 🤔EDIT: Simple solutions are the best 😅
Why is this issue closed? Its so hard to work with Prisma when you are always fighting against its type system with unpredictable results.
Definitely, that’s the workaround the validator uses:
I’m working on a codebase that relies (too) much on this - it’s heavy for my cognitive load. But there is a potential PR candidate that should solve this problem in the near (?) future: https://github.com/microsoft/TypeScript/pull/26349
Though it’s not certain that this will solve our use case here. It will depend on whether we will be allowed to use the
_as a default parameter or not. A few people already have asked for this. In a perfect world, we would be able to do:Time will tell, if this is not supported, then we will fallback to this issue https://github.com/microsoft/TypeScript/issues/10571
The TypeScript team has added this on their roadmap, but no date is yet set. This is one of the most exciting (missing) features for TypeScript today, IMO.
We just got help from a TypeScript god - Pierre-Antoine Mills @millsp, the author of ts-toolbelt (without his work, we wouldn’t have group by today). He has some ideas: https://github.com/timsuchanek/make-type/blob/master/draft.ts Unfortunately, TypeScript has an annoying limitation, that you can’t “skip” type parameters. Therefore, it looks like we have to generate the
createUserSelectandcreateUserIncludeAPI for all types.And @millsp thanks a lot for the help there! I just merged your new types and they work like a charm!
@timsuchanek What is the recommended way to create business logic wrappers around Prisma Client?
Translating function params to prisma
whereobject is easy but I can’t simply make the returned type typesafe while allowing consumers to request only selected fields (or relations):This narrows the returned object type to
{}:@timsuchanek this snippets gives a TS error 😛
It has to be explicitly
.findMany<{ select: S, where: Prisma.UserWhereInput}>(...)which adds more boilerplate if you want to add more params likerejectOnNotFound: true😞Second case, I can’t make this approach work if I want the
selectto be optional like with direct prisma client usage. If I make this parameter optional, the return type becomes{}😕Thanks @artemzakharov for reporting. As @pantharshit00 correctly noted, this is intended to not work anymore.
The reason is, that if you typecast it to the
Photon.UserSelecttype, that this is not type safe anymore. Simple example:In this example
resultwill now also includenamein the type, as the type ofselectisUserSelectwhich also has thename, even though the constselectdoesn’t have. In other words, Prisma Client is not looking into the values but just the types for the ts type validation / inference.Exactly for this situation that you described we introduced more strict type checks, that make sure, that you don’t just import
UserSelectfrom@prisma/clientand type cast the client, as that would break the type safety.The only solution we have within TypeScript for this, is, to remove the type cast of your
userSelectionvariable and let TypeScript validate things later in thefindOnecall. I know that’s not the 100% optimal solution, but for now, this is all we can do here.Something we could definitely improve - we should in the types detect this case and instead of having the empty type actually generate a nice type-level error message, that describes the problem and the solution.
I always use this trick:
Errors did not show up when only bad fields were passed:
This came from a mistake I made while writing
Exact. It is a type utility that forces a type to comply by another one - with not more and not less properties. I was trying to improve intellisense errors by doing a type cast… which caused an untested case to fail. So I rewrote it, and all errors are caught properly (with nice intellisense errors):Exacttakes any objectAand makes it comply byW. IfAhas a prop that is in not inW, we mark it as never. IfA[K]complies byW[K]we returnA[K], otherwise we returnW[K]and this will give us better intellisense errors in case of conflicts.This is a more mature
Exacttype utility than the ones proposed in https://github.com/microsoft/TypeScript/issues/12936#issuecomment-368244671 since it is able to:Community-developed
Exactutilities all fail at least one of the points (1, 2, 3) found just above (where points 1, 2, and 3 are the most important). But in the end it’s no surprise because emulating features for the compiler/checker is certainly not an easy task.(If you’re interested to see the comparison and failures here or at https://github.com/millsp/make-type/blob/master/draft.ts#L42-L79)
@TiE23 Are you able to get type hints as well when calling the function? The return type works as expected but auto completion does not seem to be working.
Oops, I think I only tested it without the
whereon my machine 🙈 I see this as a possible solution to make it more general:@millsp do you maybe have an idea how to make that more elegant? You can use https://github.com/timsuchanek/make-type/blob/master/main.ts as a playground
Thanks for the answer @Sytten! Yes I totally think we can add something there. I’ll candidate this for next sprint.