Skip to content

Commit ab0cfff

Browse files
authored
fix: make create's typing consistent with Prisma regarding to relations (#78)
1 parent f30b8d6 commit ab0cfff

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
@@ -650,8 +650,8 @@ export class InputValidator<Schema extends SchemaDef> {
650650
withoutFields: string[] = [],
651651
withoutRelationFields = false,
652652
) {
653-
const regularAndFkFields: any = {};
654-
const regularAndRelationFields: any = {};
653+
const uncheckedVariantFields: Record<string, ZodType> = {};
654+
const checkedVariantFields: Record<string, ZodType> = {};
655655
const modelDef = requireModel(this.schema, model);
656656
const hasRelation =
657657
!withoutRelationFields &&
@@ -705,7 +705,11 @@ export class InputValidator<Schema extends SchemaDef> {
705705
if (fieldDef.optional && !fieldDef.array) {
706706
fieldSchema = fieldSchema.nullable();
707707
}
708-
regularAndRelationFields[field] = fieldSchema;
708+
checkedVariantFields[field] = fieldSchema;
709+
if (fieldDef.array || !fieldDef.relation.references) {
710+
// non-owned relation
711+
uncheckedVariantFields[field] = fieldSchema;
712+
}
709713
} else {
710714
let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type);
711715

@@ -728,21 +732,22 @@ export class InputValidator<Schema extends SchemaDef> {
728732
fieldSchema = fieldSchema.nullable();
729733
}
730734

731-
regularAndFkFields[field] = fieldSchema;
735+
uncheckedVariantFields[field] = fieldSchema;
732736
if (!fieldDef.foreignKeyFor) {
733-
regularAndRelationFields[field] = fieldSchema;
737+
// non-fk field
738+
checkedVariantFields[field] = fieldSchema;
734739
}
735740
}
736741
});
737742

738743
if (!hasRelation) {
739-
return this.orArray(z.object(regularAndFkFields).strict(), canBeArray);
744+
return this.orArray(z.object(uncheckedVariantFields).strict(), canBeArray);
740745
} else {
741746
return z.union([
742-
z.object(regularAndFkFields).strict(),
743-
z.object(regularAndRelationFields).strict(),
744-
...(canBeArray ? [z.array(z.object(regularAndFkFields).strict())] : []),
745-
...(canBeArray ? [z.array(z.object(regularAndRelationFields).strict())] : []),
747+
z.object(uncheckedVariantFields).strict(),
748+
z.object(checkedVariantFields).strict(),
749+
...(canBeArray ? [z.array(z.object(uncheckedVariantFields).strict())] : []),
750+
...(canBeArray ? [z.array(z.object(checkedVariantFields).strict())] : []),
746751
]);
747752
}
748753
}

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,4 +288,33 @@ describe.each(createClientSpecs(PG_DB_NAME))('Client create tests', ({ createCli
288288
expect(u3.posts).toHaveLength(3);
289289
expect(u3.posts.map((p) => p.title)).toEqual(expect.arrayContaining(['Post1', 'Post2', 'Post4']));
290290
});
291+
292+
it('complies with Prisma checked/unchecked typing', async () => {
293+
const user = await client.user.create({
294+
data: { email: '[email protected]' },
295+
});
296+
297+
// fk and owned-relation are mutually exclusive
298+
client.post.create({
299+
// @ts-expect-error
300+
data: {
301+
authorId: user.id,
302+
title: 'title',
303+
author: { connect: { id: user.id } },
304+
},
305+
});
306+
307+
// fk can work with non-owned relation
308+
await expect(
309+
client.post.create({
310+
data: {
311+
authorId: user.id,
312+
title: 'title',
313+
comments: {
314+
create: { content: 'comment' },
315+
},
316+
},
317+
}),
318+
).toResolveTruthy();
319+
});
291320
});

0 commit comments

Comments
 (0)