diff --git a/packages/orm/src/client/crud-types.ts b/packages/orm/src/client/crud-types.ts index 1d6542a99..f6cc2767f 100644 --- a/packages/orm/src/client/crud-types.ts +++ b/packages/orm/src/client/crud-types.ts @@ -1315,8 +1315,20 @@ export type Subset = { [key in keyof T]: key extends keyof U ? T[key] : never; }; +type NoExtraKeys = T extends object ? T & { [K in Exclude]: never } : T; + +type PreserveNullish = Strict | Extract; + +type StrictArgProperty = Key extends keyof U + ? PreserveNullish, NonNullable>> + : T; + export type SelectSubset = { - [key in keyof T]: key extends keyof U ? T[key] : never; + [key in keyof T]: key extends keyof U + ? key extends 'select' | 'include' | 'omit' + ? StrictArgProperty + : T[key] + : never; } & (T extends { select: any; include: any } ? 'Please either choose `select` or `include`.' : T extends { select: any; omit: any } diff --git a/tests/e2e/orm/client-api/slicing.test.ts b/tests/e2e/orm/client-api/slicing.test.ts index 4432b58ca..0ec6360ef 100644 --- a/tests/e2e/orm/client-api/slicing.test.ts +++ b/tests/e2e/orm/client-api/slicing.test.ts @@ -216,6 +216,7 @@ describe('Query slicing tests', () => { // Profile is excluded, so selecting it should cause type error await expect( db.user.findMany({ + // @ts-expect-error Profile is excluded by slicing select: { id: true, profile: true }, }), ).toBeRejectedByValidation(['"profile"', '"select"']); @@ -223,6 +224,7 @@ describe('Query slicing tests', () => { // Comment is excluded, so selecting it should cause type error await expect( db.post.findMany({ + // @ts-expect-error Comment is excluded by slicing select: { id: true, comments: true }, }), ).toBeRejectedByValidation(['"comments"', '"select"']); @@ -295,6 +297,7 @@ describe('Query slicing tests', () => { // Profile is not included, so selecting it should cause type error await expect( db.user.findMany({ + // @ts-expect-error Profile is not included by slicing select: { id: true, profile: true }, }), ).toBeRejectedByValidation(['"profile"', '"select"']); @@ -302,6 +305,7 @@ describe('Query slicing tests', () => { // Comment is not included, so selecting it should cause type error await expect( db.post.findMany({ + // @ts-expect-error Comment is not included by slicing select: { id: true, comments: true }, }), ).toBeRejectedByValidation(['"comments"', '"select"']); diff --git a/tests/e2e/orm/client-api/unsupported.test.ts b/tests/e2e/orm/client-api/unsupported.test.ts index ad1daba45..da1d6ed69 100644 --- a/tests/e2e/orm/client-api/unsupported.test.ts +++ b/tests/e2e/orm/client-api/unsupported.test.ts @@ -205,6 +205,7 @@ describe('Unsupported field exclusion - Zod runtime validation', () => { it('rejects Unsupported fields in select', async () => { // valid call await db.item.findMany({ select: { id: true, name: true } }); + // @ts-expect-error data (Unsupported) should not be in select await expect(db.item.findMany({ select: { data: true } })).toBeRejectedByValidation(); }); diff --git a/tests/e2e/orm/schemas/typing/typecheck.ts b/tests/e2e/orm/schemas/typing/typecheck.ts index 9f8b2aa86..5262d552b 100644 --- a/tests/e2e/orm/schemas/typing/typecheck.ts +++ b/tests/e2e/orm/schemas/typing/typecheck.ts @@ -74,6 +74,16 @@ async function find() { }); console.log(user2.email); console.log(user2.profile?.age); + // @ts-expect-error name was not selected + console.log(user2.name); + + await client.user.findMany({ + select: { + email: true, + // @ts-expect-error invalid select field + missingField: true, + }, + }); await client.user.findUnique({ // @ts-expect-error expect unique filter