Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to the $queryRaw function's type signature #23987

Open
LinusU opened this issue Apr 26, 2024 · 0 comments
Open

Improvements to the $queryRaw function's type signature #23987

LinusU opened this issue Apr 26, 2024 · 0 comments
Labels
kind/improvement An improvement to existing feature and code. team/client Issue for team Client. tech/typescript Issue for tech TypeScript. topic: client types Types in Prisma Client topic: raw $queryRaw(Unsafe) and $executeRaw(Unsafe): https://www.prisma.io/docs/concepts/components/prisma-cli

Comments

@LinusU
Copy link

LinusU commented Apr 26, 2024

Problem

Using $queryRaw currently gives you no type safety at all. This is frustrating in a number of scenarios, where it would be perfectly possible to get the information without compromising on type accuracy.

Here are some examples:

const rows = await postgres.$queryRaw`SELECT "id" FROM "User" WHERE "email" = ${email}`
const emailExists = rows.length > 0 // ERROR: cannot read property length of unknown
const rows = await postgres.$queryRaw`SELECT "name" FROM "User"`

for (const row of rows) { // ERROR: cannot iterate over unknown
  console.log(row)
}

There is also some frustration when specifying the type of the rows using the functions type parameters. $queryRaw will always return an array, yet it's possible to use the type parameters to specify something that isn't an array:

// The problem starts here, in order to work with Prisma 5.x you need to specify Array<{ name: string }> as the type parameter, not just the object
const rows = `await postgres.$queryRaw<{ name: string }>`SELECT "name" FROM "User"`

for (const row of rows) { // ERROR: cannot iterate over object
  console.log(row)
}

Suggested solution

I propose that we change the function signature as follows:

- $queryRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<T>
+ $queryRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<T[]>

This would make all the examples above work.

Note that this would be a breaking change, since users of the current type parameter already specifies the extra Array<...> that would be superfluous after this change.

Alternatives

Another alternativ would be to go one step further, as discussed in #15263. However, my experience with Record is that it's not always desired, since it can potentially lead to some problems when trying to do guarded type casts. I think that this would need additional investigation before pursuing:

- $queryRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<T>
+ $queryRaw<T extends Record<string, unknown> = Record<string, unknown>>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<T[]>

Another alternative would be to remove the ability to specify a type parameter. I actually would prefer this solution since the current disguises a type assertion, and is even listed as a common misstake in the DefinitelyTyped guide.

This is especially problematic for serious organizations that use linting to disallow unsafe type casts, since this usage cannot be checked by the linter.

- $queryRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<T>
+ $queryRaw(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<unknown[]>

For current users, this would be slightly more work to update, but the upside would be that they would be made aware of their unchecked type assertions! It would be something like this:

- await postgres.$queryRaw<Array<{ id: string }>>`SELECT "id" FROM "User"`
+ await postgres.$queryRaw`SELECT "id" FROM "User"` as Array<{ id: string }>

Personally, I think that this would be the best approach. But unfortunately, when maintaining types, I've seen a lot of pushback from users that prefer things to be easy rather than correct. And since this would be a (slightly) larger change for current users, I'm including it as an alternative instead of the main proposal.

Additional context

Related bug: #15263

@Weakky Weakky added kind/improvement An improvement to existing feature and code. tech/typescript Issue for tech TypeScript. team/client Issue for team Client. topic: raw $queryRaw(Unsafe) and $executeRaw(Unsafe): https://www.prisma.io/docs/concepts/components/prisma-cli topic: client types Types in Prisma Client labels Apr 26, 2024
@janpio janpio changed the title Improvements to the $queryRaw function's type signature Improvements to the $queryRaw function's type signature Apr 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/improvement An improvement to existing feature and code. team/client Issue for team Client. tech/typescript Issue for tech TypeScript. topic: client types Types in Prisma Client topic: raw $queryRaw(Unsafe) and $executeRaw(Unsafe): https://www.prisma.io/docs/concepts/components/prisma-cli
Projects
None yet
Development

No branches or pull requests

2 participants