diff --git a/packages/runtime/src/client/crud-types.ts b/packages/runtime/src/client/crud-types.ts index ada75549..4643991d 100644 --- a/packages/runtime/src/client/crud-types.ts +++ b/packages/runtime/src/client/crud-types.ts @@ -528,12 +528,9 @@ export type CreateArgs omit?: OmitFields; }; -export type CreateManyArgs> = CreateManyPayload< - Schema, - Model ->; +export type CreateManyArgs> = CreateManyInput; -export type CreateManyAndReturnArgs> = CreateManyPayload< +export type CreateManyAndReturnArgs> = CreateManyInput< Schema, Model > & { @@ -597,8 +594,13 @@ type CreateRelationPayload; -type CreateWithFKInput> = CreateScalarPayload & - CreateFKPayload; +type CreateWithFKInput> = + // scalar fields + CreateScalarPayload & + // fk fields + CreateFKPayload & + // non-owned relations + CreateWithNonOwnedRelationPayload; type CreateWithRelationInput> = CreateScalarPayload< Schema, @@ -606,6 +608,14 @@ type CreateWithRelationInput & CreateRelationPayload; +type CreateWithNonOwnedRelationPayload> = OptionalWrap< + Schema, + Model, + { + [Key in NonOwnedRelationFields]: CreateRelationFieldPayload; + } +>; + type ConnectOrCreatePayload< Schema extends SchemaDef, Model extends GetModels, @@ -615,7 +625,7 @@ type ConnectOrCreatePayload< create: CreateInput; }; -export type CreateManyPayload< +export type CreateManyInput< Schema extends SchemaDef, Model extends GetModels, Without extends string = never, @@ -643,7 +653,7 @@ type NestedCreateManyInput< Schema extends SchemaDef, Model extends GetModels, Field extends RelationFields, -> = CreateManyPayload, OppositeRelationAndFK>; +> = CreateManyInput, OppositeRelationAndFK>; //#endregion @@ -1078,3 +1088,13 @@ type NestedDeleteManyInput< > = OrArray, true>>; // #endregion + +// #region Utilities + +type NonOwnedRelationFields> = keyof { + [Key in RelationFields as GetField['relation'] extends { references: unknown[] } + ? never + : Key]: Key; +}; + +// #endregion diff --git a/packages/runtime/src/client/crud/validator.ts b/packages/runtime/src/client/crud/validator.ts index 754d53d4..9e443cf6 100644 --- a/packages/runtime/src/client/crud/validator.ts +++ b/packages/runtime/src/client/crud/validator.ts @@ -650,8 +650,8 @@ export class InputValidator { withoutFields: string[] = [], withoutRelationFields = false, ) { - const regularAndFkFields: any = {}; - const regularAndRelationFields: any = {}; + const uncheckedVariantFields: Record = {}; + const checkedVariantFields: Record = {}; const modelDef = requireModel(this.schema, model); const hasRelation = !withoutRelationFields && @@ -705,7 +705,11 @@ export class InputValidator { if (fieldDef.optional && !fieldDef.array) { fieldSchema = fieldSchema.nullable(); } - regularAndRelationFields[field] = fieldSchema; + checkedVariantFields[field] = fieldSchema; + if (fieldDef.array || !fieldDef.relation.references) { + // non-owned relation + uncheckedVariantFields[field] = fieldSchema; + } } else { let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type); @@ -728,21 +732,22 @@ export class InputValidator { fieldSchema = fieldSchema.nullable(); } - regularAndFkFields[field] = fieldSchema; + uncheckedVariantFields[field] = fieldSchema; if (!fieldDef.foreignKeyFor) { - regularAndRelationFields[field] = fieldSchema; + // non-fk field + checkedVariantFields[field] = fieldSchema; } } }); if (!hasRelation) { - return this.orArray(z.object(regularAndFkFields).strict(), canBeArray); + return this.orArray(z.object(uncheckedVariantFields).strict(), canBeArray); } else { return z.union([ - z.object(regularAndFkFields).strict(), - z.object(regularAndRelationFields).strict(), - ...(canBeArray ? [z.array(z.object(regularAndFkFields).strict())] : []), - ...(canBeArray ? [z.array(z.object(regularAndRelationFields).strict())] : []), + z.object(uncheckedVariantFields).strict(), + z.object(checkedVariantFields).strict(), + ...(canBeArray ? [z.array(z.object(uncheckedVariantFields).strict())] : []), + ...(canBeArray ? [z.array(z.object(checkedVariantFields).strict())] : []), ]); } } diff --git a/packages/runtime/test/client-api/create.test.ts b/packages/runtime/test/client-api/create.test.ts index 51e0c06d..6ac87637 100644 --- a/packages/runtime/test/client-api/create.test.ts +++ b/packages/runtime/test/client-api/create.test.ts @@ -288,4 +288,33 @@ describe.each(createClientSpecs(PG_DB_NAME))('Client create tests', ({ createCli expect(u3.posts).toHaveLength(3); expect(u3.posts.map((p) => p.title)).toEqual(expect.arrayContaining(['Post1', 'Post2', 'Post4'])); }); + + it('complies with Prisma checked/unchecked typing', async () => { + const user = await client.user.create({ + data: { email: 'u1@test.com' }, + }); + + // fk and owned-relation are mutually exclusive + client.post.create({ + // @ts-expect-error + data: { + authorId: user.id, + title: 'title', + author: { connect: { id: user.id } }, + }, + }); + + // fk can work with non-owned relation + await expect( + client.post.create({ + data: { + authorId: user.id, + title: 'title', + comments: { + create: { content: 'comment' }, + }, + }, + }), + ).toResolveTruthy(); + }); });