perf: add z.ZodType annotations to high-impact schemas (#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>
This commit is contained in:
Keith Williams
2025-12-21 23:15:04 -03:00
committed by GitHub
co-authored by Devin AI
parent ee6989a575
commit 851cca25aa
3 changed files with 100 additions and 6 deletions
+30 -2
View File
@@ -4,6 +4,26 @@ import { raqbQueryValueSchema } from "@calcom/lib/raqb/zod";
import { routingFormAppDataSchemas } from "./appDataSchemas";
export type FieldOption = {
label: string;
id: string | null;
};
export type TNonRouterField = {
id: string;
label: string;
identifier?: string;
placeholder?: string;
type: string;
/** @deprecated in favour of `options` */
selectText?: string;
required?: boolean;
deleted?: boolean;
options?: FieldOption[];
};
// Note: zodNonRouterField is NOT annotated with z.ZodType because it uses .extend() below
// which requires the full ZodObject type to be preserved
export const zodNonRouterField = z.object({
id: z.string(),
label: z.string(),
@@ -29,13 +49,21 @@ export const zodNonRouterField = z.object({
.optional(),
});
export type TRouterField = TNonRouterField & {
routerId: string;
};
// Note: zodRouterField is NOT annotated with z.ZodType because it uses .extend() below
export const zodRouterField = zodNonRouterField.extend({
routerId: z.string(),
});
export type TField = TNonRouterField | TRouterField;
export type TFields = TField[] | undefined;
// 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
export const zodField = z.union([zodRouterField, zodNonRouterField]);
export const zodFields = z.array(zodField).optional();
export const zodField: z.ZodType<TField> = z.union([zodRouterField, zodNonRouterField]);
export const zodFields: z.ZodType<TFields> = z.array(zodField).optional();
export const zodNonRouterFieldView = zodNonRouterField;
export const zodRouterFieldView = zodRouterField.extend({
+58 -3
View File
@@ -3,7 +3,21 @@ import { z } from "zod";
import { SchedulingType } from "@calcom/prisma/enums";
import { eventTypeLocations, EventTypeMetaDataSchema, eventTypeSlug } from "@calcom/prisma/zod-utils";
const calVideoSettingsSchema = z
type CalVideoSettings =
| {
disableRecordingForGuests?: boolean | null;
disableRecordingForOrganizer?: boolean | null;
enableAutomaticTranscription?: boolean | null;
enableAutomaticRecordingForOrganizer?: boolean | null;
disableTranscriptionForGuests?: boolean | null;
disableTranscriptionForOrganizer?: boolean | null;
redirectUrlOnExit?: string | null;
requireEmailForGuests?: boolean | null;
}
| null
| undefined;
const calVideoSettingsSchema: z.ZodType<CalVideoSettings> = z
.object({
disableRecordingForGuests: z.boolean().nullish(),
disableRecordingForOrganizer: z.boolean().nullish(),
@@ -17,7 +31,29 @@ const calVideoSettingsSchema = z
.optional()
.nullable();
export const EventTypeDuplicateInput = z
type EventTypeLocation = {
type: string;
address?: string;
link?: string;
displayLocationPublicly?: boolean;
hostPhoneNumber?: string;
credentialId?: number;
teamName?: string;
customLabel?: string;
};
type EventTypeMetadata = z.infer<typeof EventTypeMetaDataSchema>;
export type TEventTypeDuplicateInput = {
id: number;
slug: string;
title: string;
description: string;
length: number;
teamId?: number | null;
};
export const EventTypeDuplicateInput: z.ZodType<TEventTypeDuplicateInput> = z
.object({
id: z.number(),
slug: z.string(),
@@ -28,7 +64,26 @@ export const EventTypeDuplicateInput = z
})
.strict();
export const createEventTypeInput = z
export type TCreateEventTypeInput = {
title: string;
slug: string;
description?: string | null;
length: number;
hidden?: boolean;
teamId?: number | null;
schedulingType?: SchedulingType | null;
locations?: EventTypeLocation[];
metadata?: EventTypeMetadata;
disableGuests?: boolean;
slotInterval?: number | null;
minimumBookingNotice?: number;
beforeEventBuffer?: number;
afterEventBuffer?: number;
scheduleId?: number;
calVideoSettings?: CalVideoSettings;
};
export const createEventTypeInput: z.ZodType<TCreateEventTypeInput> = z
.object({
title: z.string().trim().min(1),
slug: eventTypeSlug,
+12 -1
View File
@@ -327,7 +327,18 @@ export const bookingResponses = z
export type BookingResponses = z.infer<typeof bookingResponses>;
export const eventTypeLocations = z.array(
export type EventTypeLocation = {
type: string;
address?: string;
link?: string;
displayLocationPublicly?: boolean;
hostPhoneNumber?: string;
credentialId?: number;
teamName?: string;
customLabel?: string;
};
export const eventTypeLocations: z.ZodType<EventTypeLocation[]> = z.array(
z.object({
// TODO: Couldn't find a way to make it a union of types from App Store locations
// Creating a dynamic union by iterating over the object doesn't seem to make TS happy