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:
co-authored by
Devin AI
parent
ee6989a575
commit
851cca25aa
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user