Skip to content

Commit 851cca2

Browse files
perf: add z.ZodType annotations to high-impact schemas (calcom#26114)
* perf: add z.ZodType annotations to high-impact schemas Add explicit type annotations to reduce TypeScript type inference overhead and .d.ts file bloat in the tRPC package. Changes: - packages/prisma/zod-utils.ts: Add EventTypeLocation type and annotate eventTypeLocations schema - packages/features/eventtypes/lib/schemas.ts: Add TEventTypeDuplicateInput and TCreateEventTypeInput types with annotations - packages/app-store/routing-forms/zod.ts: Add FieldOption, TNonRouterField, TRouterField, TField, TFields types with annotations for zodField and zodFields These schemas feed into multiple tRPC routers and were identified as high-impact targets for reducing type checking time and declaration file sizes. Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: replace z.infer with explicit types and define EventTypeLocation locally Address PR feedback: - Replace z.infer<typeof calVideoSettingsSchema> with explicit CalVideoSettings type - Define EventTypeLocation type locally instead of importing from prisma - Add z.ZodType annotation to calVideoSettingsSchema Note: Still importing Zod schemas from @calcom/prisma/zod-utils for validation. Awaiting guidance on whether to move those to @calcom/lib. Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent ee6989a commit 851cca2

3 files changed

Lines changed: 100 additions & 6 deletions

File tree

packages/app-store/routing-forms/zod.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,26 @@ import { raqbQueryValueSchema } from "@calcom/lib/raqb/zod";
44

55
import { routingFormAppDataSchemas } from "./appDataSchemas";
66

7+
export type FieldOption = {
8+
label: string;
9+
id: string | null;
10+
};
11+
12+
export type TNonRouterField = {
13+
id: string;
14+
label: string;
15+
identifier?: string;
16+
placeholder?: string;
17+
type: string;
18+
/** @deprecated in favour of `options` */
19+
selectText?: string;
20+
required?: boolean;
21+
deleted?: boolean;
22+
options?: FieldOption[];
23+
};
24+
25+
// Note: zodNonRouterField is NOT annotated with z.ZodType because it uses .extend() below
26+
// which requires the full ZodObject type to be preserved
727
export const zodNonRouterField = z.object({
828
id: z.string(),
929
label: z.string(),
@@ -29,13 +49,21 @@ export const zodNonRouterField = z.object({
2949
.optional(),
3050
});
3151

52+
export type TRouterField = TNonRouterField & {
53+
routerId: string;
54+
};
55+
56+
// Note: zodRouterField is NOT annotated with z.ZodType because it uses .extend() below
3257
export const zodRouterField = zodNonRouterField.extend({
3358
routerId: z.string(),
3459
});
3560

61+
export type TField = TNonRouterField | TRouterField;
62+
export type TFields = TField[] | undefined;
63+
3664
// This ordering is important - If routerId is present then it should be in the parsed object. Moving zodNonRouterField to first position doesn't do that
37-
export const zodField = z.union([zodRouterField, zodNonRouterField]);
38-
export const zodFields = z.array(zodField).optional();
65+
export const zodField: z.ZodType<TField> = z.union([zodRouterField, zodNonRouterField]);
66+
export const zodFields: z.ZodType<TFields> = z.array(zodField).optional();
3967

4068
export const zodNonRouterFieldView = zodNonRouterField;
4169
export const zodRouterFieldView = zodRouterField.extend({

packages/features/eventtypes/lib/schemas.ts

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,21 @@ import { z } from "zod";
33
import { SchedulingType } from "@calcom/prisma/enums";
44
import { eventTypeLocations, EventTypeMetaDataSchema, eventTypeSlug } from "@calcom/prisma/zod-utils";
55

6-
const calVideoSettingsSchema = z
6+
type CalVideoSettings =
7+
| {
8+
disableRecordingForGuests?: boolean | null;
9+
disableRecordingForOrganizer?: boolean | null;
10+
enableAutomaticTranscription?: boolean | null;
11+
enableAutomaticRecordingForOrganizer?: boolean | null;
12+
disableTranscriptionForGuests?: boolean | null;
13+
disableTranscriptionForOrganizer?: boolean | null;
14+
redirectUrlOnExit?: string | null;
15+
requireEmailForGuests?: boolean | null;
16+
}
17+
| null
18+
| undefined;
19+
20+
const calVideoSettingsSchema: z.ZodType<CalVideoSettings> = z
721
.object({
822
disableRecordingForGuests: z.boolean().nullish(),
923
disableRecordingForOrganizer: z.boolean().nullish(),
@@ -17,7 +31,29 @@ const calVideoSettingsSchema = z
1731
.optional()
1832
.nullable();
1933

20-
export const EventTypeDuplicateInput = z
34+
type EventTypeLocation = {
35+
type: string;
36+
address?: string;
37+
link?: string;
38+
displayLocationPublicly?: boolean;
39+
hostPhoneNumber?: string;
40+
credentialId?: number;
41+
teamName?: string;
42+
customLabel?: string;
43+
};
44+
45+
type EventTypeMetadata = z.infer<typeof EventTypeMetaDataSchema>;
46+
47+
export type TEventTypeDuplicateInput = {
48+
id: number;
49+
slug: string;
50+
title: string;
51+
description: string;
52+
length: number;
53+
teamId?: number | null;
54+
};
55+
56+
export const EventTypeDuplicateInput: z.ZodType<TEventTypeDuplicateInput> = z
2157
.object({
2258
id: z.number(),
2359
slug: z.string(),
@@ -28,7 +64,26 @@ export const EventTypeDuplicateInput = z
2864
})
2965
.strict();
3066

31-
export const createEventTypeInput = z
67+
export type TCreateEventTypeInput = {
68+
title: string;
69+
slug: string;
70+
description?: string | null;
71+
length: number;
72+
hidden?: boolean;
73+
teamId?: number | null;
74+
schedulingType?: SchedulingType | null;
75+
locations?: EventTypeLocation[];
76+
metadata?: EventTypeMetadata;
77+
disableGuests?: boolean;
78+
slotInterval?: number | null;
79+
minimumBookingNotice?: number;
80+
beforeEventBuffer?: number;
81+
afterEventBuffer?: number;
82+
scheduleId?: number;
83+
calVideoSettings?: CalVideoSettings;
84+
};
85+
86+
export const createEventTypeInput: z.ZodType<TCreateEventTypeInput> = z
3287
.object({
3388
title: z.string().trim().min(1),
3489
slug: eventTypeSlug,

packages/prisma/zod-utils.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,18 @@ export const bookingResponses = z
327327

328328
export type BookingResponses = z.infer<typeof bookingResponses>;
329329

330-
export const eventTypeLocations = z.array(
330+
export type EventTypeLocation = {
331+
type: string;
332+
address?: string;
333+
link?: string;
334+
displayLocationPublicly?: boolean;
335+
hostPhoneNumber?: string;
336+
credentialId?: number;
337+
teamName?: string;
338+
customLabel?: string;
339+
};
340+
341+
export const eventTypeLocations: z.ZodType<EventTypeLocation[]> = z.array(
331342
z.object({
332343
// TODO: Couldn't find a way to make it a union of types from App Store locations
333344
// Creating a dynamic union by iterating over the object doesn't seem to make TS happy

0 commit comments

Comments
 (0)