Skip to content

Commit

Permalink
fix(client): make nested omit types work (#25900)
Browse files Browse the repository at this point in the history
When we have nested `select` or `include`, TypeScript at some point
gives up remembering what keys `A` has exactly and discards the `omit`
for whatever reason. Re-adding the `omit` key at the point it happens
using the `A & { omit: A['omit'] }` hack makes TypeScript a little bit
less forgetful.

Closes: prisma/team-orm#1442
Fixes: #24835

Co-authored-by: Jacek Malec <malec@prisma.io>
  • Loading branch information
aqrln and jacek-prisma authored Dec 20, 2024
1 parent 509b064 commit ebda0b9
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 4 deletions.
17 changes: 13 additions & 4 deletions packages/client/src/runtime/core/types/exported/Result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,16 @@ export type GetFindResult<P extends OperationPayload, A, ClientOptions> =
: K extends '_count'
? Count<P['objects']>
: never
} & (A extends { include: any } & Record<string, unknown> ? DefaultSelection<P, A, ClientOptions> : unknown)
} & (
A extends { include: any } & Record<string, unknown>
// The `A & { omit: ['omit'] }` hack is necessary because otherwise, when we have nested `select` or `include`,
// TypeScript at some point gives up remembering what keys `A` has exactly and discards the `omit` for whatever
// reason. Splitting the top-level conditional type above into two separate branches and handling `select` and
// `include` separately so we don't need to use `A extends { include: any } & Record<string, unknown>` above in
// this branch here makes zero difference. Re-adding the `omit` key here makes TypeScript remember it.
? DefaultSelection<P, A & { omit: A['omit'] }, ClientOptions>
: unknown
)
: DefaultSelection<P, A, ClientOptions>

// prettier-ignore
Expand All @@ -83,7 +92,7 @@ export type SelectablePayloadFields<K extends PropertyKey, O> =

// prettier-ignore
export type SelectField<P extends SelectablePayloadFields<any, any>, K extends PropertyKey> =
P extends { objects: Record<K, any> }
P extends { objects: Record<K, any> }
? P['objects'][K]
: P extends { composites: Record<K, any> }
? P['composites'][K]
Expand All @@ -99,7 +108,7 @@ export type DefaultSelection<Payload extends OperationPayload, Args = {}, Client

// prettier-ignore
export type UnwrapPayload<P> = {} extends P ? unknown : {
[K in keyof P]:
[K in keyof P]:
P[K] extends { scalars: infer S, composites: infer C }[]
? Array<S & UnwrapPayload<C>>
: P[K] extends { scalars: infer S, composites: infer C } | null
Expand Down Expand Up @@ -131,7 +140,7 @@ export type GetBatchResult = { count: number }
export type GetGroupByResult<P extends OperationPayload, A> =
A extends { by: string[] }
? Array<GetAggregateResult<P, A> & { [K in A['by'][number]]: P['scalars'][K] }>
: A extends { by: string }
: A extends { by: string }
? Array<GetAggregateResult<P, A> & { [K in A['by']]: P['scalars'][K]}>
: {}[]

Expand Down
4 changes: 4 additions & 0 deletions packages/client/tests/functional/24835-omit-error/_matrix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { defineMatrix } from '../_utils/defineMatrix'
import { allProviders } from '../_utils/providers'

export default defineMatrix(() => [allProviders])
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { idForProvider } from '../../_utils/idForProvider'
import testMatrix from '../_matrix'

export default testMatrix.setupSchema(({ provider }) => {
return /* Prisma */ `
generator client {
provider = "prisma-client-js"
previewFeatures = ["omitApi"]
}
datasource db {
provider = "${provider}"
url = env("DATABASE_URI_${provider}")
}
model A {
id ${idForProvider(provider)}
model_b B[]
}
model B {
id ${idForProvider(provider)}
a_id String
a A @relation(fields: [a_id], references: [id])
private_field String
c_id String
c C @relation(fields: [c_id], references: [id])
}
model C {
id ${idForProvider(provider)}
public_field String
B B[]
}
`
})
26 changes: 26 additions & 0 deletions packages/client/tests/functional/24835-omit-error/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import testMatrix from './_matrix'
// @ts-ignore
import type { PrismaClient } from './node_modules/@prisma/client'

declare let prisma: PrismaClient

testMatrix.setupTestSuite(() => {
test('have omitted field as never', async () => {
const example = await prisma.a.findFirst({
include: {
model_b: {
include: {
c: true,
},
omit: {
private_field: true,
},
},
},
omit: { id: true },
})

// @ts-expect-error
example?.model_b[0].private_field
})
})

0 comments on commit ebda0b9

Please sign in to comment.