Skip to content

Commit 209e92d

Browse files
committed
fix: make create's typing consistent with Prisma regarding to relations
1 parent b656805 commit 209e92d

File tree

3 files changed

+73
-19
lines changed

3 files changed

+73
-19
lines changed

packages/runtime/src/client/crud-types.ts

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -528,12 +528,9 @@ export type CreateArgs<Schema extends SchemaDef, Model extends GetModels<Schema>
528528
omit?: OmitFields<Schema, Model>;
529529
};
530530

531-
export type CreateManyArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateManyPayload<
532-
Schema,
533-
Model
534-
>;
531+
export type CreateManyArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateManyInput<Schema, Model>;
535532

536-
export type CreateManyAndReturnArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateManyPayload<
533+
export type CreateManyAndReturnArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateManyInput<
537534
Schema,
538535
Model
539536
> & {
@@ -597,15 +594,28 @@ type CreateRelationPayload<Schema extends SchemaDef, Model extends GetModels<Sch
597594
}
598595
>;
599596

600-
type CreateWithFKInput<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateScalarPayload<Schema, Model> &
601-
CreateFKPayload<Schema, Model>;
597+
type CreateWithFKInput<Schema extends SchemaDef, Model extends GetModels<Schema>> =
598+
// scalar fields
599+
CreateScalarPayload<Schema, Model> &
600+
// fk fields
601+
CreateFKPayload<Schema, Model> &
602+
// non-owned relations
603+
CreateWithNonOwnedRelationPayload<Schema, Model>;
602604

603605
type CreateWithRelationInput<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateScalarPayload<
604606
Schema,
605607
Model
606608
> &
607609
CreateRelationPayload<Schema, Model>;
608610

611+
type CreateWithNonOwnedRelationPayload<Schema extends SchemaDef, Model extends GetModels<Schema>> = OptionalWrap<
612+
Schema,
613+
Model,
614+
{
615+
[Key in NonOwnedRelationFields<Schema, Model>]: CreateRelationFieldPayload<Schema, Model, Key>;
616+
}
617+
>;
618+
609619
type ConnectOrCreatePayload<
610620
Schema extends SchemaDef,
611621
Model extends GetModels<Schema>,
@@ -615,7 +625,7 @@ type ConnectOrCreatePayload<
615625
create: CreateInput<Schema, Model, Without>;
616626
};
617627

618-
export type CreateManyPayload<
628+
export type CreateManyInput<
619629
Schema extends SchemaDef,
620630
Model extends GetModels<Schema>,
621631
Without extends string = never,
@@ -643,7 +653,7 @@ type NestedCreateManyInput<
643653
Schema extends SchemaDef,
644654
Model extends GetModels<Schema>,
645655
Field extends RelationFields<Schema, Model>,
646-
> = CreateManyPayload<Schema, RelationFieldType<Schema, Model, Field>, OppositeRelationAndFK<Schema, Model, Field>>;
656+
> = CreateManyInput<Schema, RelationFieldType<Schema, Model, Field>, OppositeRelationAndFK<Schema, Model, Field>>;
647657

648658
//#endregion
649659

@@ -1078,3 +1088,13 @@ type NestedDeleteManyInput<
10781088
> = OrArray<WhereInput<Schema, RelationFieldType<Schema, Model, Field>, true>>;
10791089

10801090
// #endregion
1091+
1092+
// #region Utilities
1093+
1094+
type NonOwnedRelationFields<Schema extends SchemaDef, Model extends GetModels<Schema>> = keyof {
1095+
[Key in RelationFields<Schema, Model> as GetField<Schema, Model, Key>['relation'] extends { references: unknown[] }
1096+
? never
1097+
: Key]: Key;
1098+
};
1099+
1100+
// #endregion

packages/runtime/src/client/crud/validator.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -633,8 +633,8 @@ export class InputValidator<Schema extends SchemaDef> {
633633
withoutFields: string[] = [],
634634
withoutRelationFields = false,
635635
) {
636-
const regularAndFkFields: any = {};
637-
const regularAndRelationFields: any = {};
636+
const uncheckedVariantFields: any = {};
637+
const checkedVariantFields: any = {};
638638
const modelDef = requireModel(this.schema, model);
639639
const hasRelation =
640640
!withoutRelationFields &&
@@ -688,7 +688,11 @@ export class InputValidator<Schema extends SchemaDef> {
688688
if (fieldDef.optional && !fieldDef.array) {
689689
fieldSchema = fieldSchema.nullable();
690690
}
691-
regularAndRelationFields[field] = fieldSchema;
691+
checkedVariantFields[field] = fieldSchema;
692+
if (fieldDef.array || !fieldDef.relation.references) {
693+
// non-owned relation
694+
uncheckedVariantFields[field] = fieldSchema;
695+
}
692696
} else {
693697
let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type);
694698

@@ -711,21 +715,22 @@ export class InputValidator<Schema extends SchemaDef> {
711715
fieldSchema = fieldSchema.nullable();
712716
}
713717

714-
regularAndFkFields[field] = fieldSchema;
718+
uncheckedVariantFields[field] = fieldSchema;
715719
if (!fieldDef.foreignKeyFor) {
716-
regularAndRelationFields[field] = fieldSchema;
720+
// non-fk field
721+
checkedVariantFields[field] = fieldSchema;
717722
}
718723
}
719724
});
720725

721726
if (!hasRelation) {
722-
return this.orArray(z.object(regularAndFkFields).strict(), canBeArray);
727+
return this.orArray(z.object(uncheckedVariantFields).strict(), canBeArray);
723728
} else {
724729
return z.union([
725-
z.object(regularAndFkFields).strict(),
726-
z.object(regularAndRelationFields).strict(),
727-
...(canBeArray ? [z.array(z.object(regularAndFkFields).strict())] : []),
728-
...(canBeArray ? [z.array(z.object(regularAndRelationFields).strict())] : []),
730+
z.object(uncheckedVariantFields).strict(),
731+
z.object(checkedVariantFields).strict(),
732+
...(canBeArray ? [z.array(z.object(uncheckedVariantFields).strict())] : []),
733+
...(canBeArray ? [z.array(z.object(checkedVariantFields).strict())] : []),
729734
]);
730735
}
731736
}

packages/runtime/test/client-api/create.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,4 +306,33 @@ describe.each(createClientSpecs(PG_DB_NAME))('Client create tests', ({ createCli
306306
}),
307307
).rejects.toThrow(QueryError);
308308
});
309+
310+
it('complies with Prisma checked/unchecked typing', async () => {
311+
const user = await client.user.create({
312+
data: { email: '[email protected]' },
313+
});
314+
315+
// fk and owned-relation are mutually exclusive
316+
client.post.create({
317+
// @ts-expect-error
318+
data: {
319+
authorId: user.id,
320+
title: 'title',
321+
author: { connect: { id: user.id } },
322+
},
323+
});
324+
325+
// fk can work with non-owned relation
326+
await expect(
327+
client.post.create({
328+
data: {
329+
authorId: user.id,
330+
title: 'title',
331+
comments: {
332+
create: { content: 'comment' },
333+
},
334+
},
335+
}),
336+
).toResolveTruthy();
337+
});
309338
});

0 commit comments

Comments
 (0)