如何从Zod Schema(含联合/交叉类型)提取键?
如何从Zod Schema(含联合、交叉类型)中提取所有键?
我定义了如下Zod对象:
const schema = z.object({ firstName: z.string().min(1, 'Required'), middleName: z.string().optional(), lastName: z.string().min(1, 'Required'), phoneCountryCode: z.string().min(1, 'Required'), phoneNumber: z.string().min(1, 'Required'), countryOfResidence: z.string().min(1, 'Required'), });
请问怎么从这个ZodObject中提取键?
我知道可以先定义原始Shape对象再传入z.object:
const schemaRawShape = { firstName: z.string().min(1, 'Required'), middleName: z.string().optional(), lastName: z.string().min(1, 'Required'), phoneCountryCode: z.string().min(1, 'Required'), phoneNumber: z.string().min(1, 'Required'), countryOfResidence: z.string().min(1, 'Required'), } satisfies z.ZodRawShape const schema = z.object(schemaRawShape); const keys = Object.keys(schemaRawShape)
但当Schema变得复杂(比如涉及联合、交叉类型)时,这种方式的可读性会变差。
补充说明:需要支持复杂Schema类型
我需要能从联合、交叉等复杂Schema中提取键。比如下面用.and()生成的交叉类型Schema没有shape属性,直接访问会报错:
import { z } from "https://esm.sh/zod@3.22.4"; const schema1 = z.object({ firstName: z.string().min(1, "Required"), middleName: z.string().optional(), lastName: z.string().min(1, "Required"), phoneCountryCode: z.string().min(1, "Required"), phoneNumber: z.string().min(1, "Required"), countryOfResidence: z.string().min(1, "Required"), }); const schema2 = z.object({ more: z.string() }) const schema = schema1.and(schema2) const propertyNames = Object.keys(schema.shape); // 此处会报错 console.log(propertyNames);
完整复杂Schema示例
import { differenceInYears } from 'date-fns'; import * as z from 'zod'; const password = z .string() .min(10, 'Must be at least 10 characters long') .regex(/\d/g, 'Must contain a number') .regex(/[a-z]/g, 'Must contain a lower case character') .regex(/[A-Z]/g, 'Must contain an upper case character') .regex(/[^\w\d]/g, 'Must contain a special character'); const infoStepSchemaCommon = z.object({ firstName: z.string().min(1, 'Required'), middleName: z.string().optional(), lastName: z.string().min(1, 'Required'), phoneCountryCode: z.string().min(1, 'Required'), phoneNumber: z.string().min(1, 'Required'), countryOfResidence: z.string().min(1, 'Required'), }); const coerceNumber = z.coerce.number({ required_error: 'Required', invalid_type_error: 'Must be a number', }); const ageRestrictionString = 'Must be at least 12 years old'; const infoStepSchemaWithoutNationalId = z .object({ hasNationalId: z.literal(false).optional(), birthMonth: coerceNumber.min(1).max(12), birthDay: coerceNumber.min(1).max(31, 'No month has more than 31 days'), birthYear: coerceNumber.min(1900).max(new Date().getFullYear()), }) .refine( (d) => differenceInYears( new Date(), new Date(d.birthYear, d.birthMonth - 1, d.birthDay) ) >= 12, { message: ageRestrictionString, path: ['birthYear'] } ) .and(infoStepSchemaCommon); const infoStepSchemaWithNationalId = z .object({ hasNationalId: z.literal(true), nationalId: z .string() .min(1, 'Required') .min(10, 'Must contain at least 10 digits') .max(10, 'Must not contain more than 10 digits') .regex(/^\d+$/, 'Must only contain numbers') }) .and(infoStepSchemaCommon); export const emailStepSchema = z.object({ email: z.string().min(1, 'Required').email(), }); export const infoStepSchema = infoStepSchemaWithoutNationalId.or( infoStepSchemaWithNationalId ); export const passwordStepSchema = z .object({ languageId: z.string(), password, passwordAgain: password, privacyPolicyAccepted: z.coerce .boolean() .refine((v) => v, 'Must accept to continue'), clubConditionsAccepted: z.coerce .boolean() .refine((v) => v, 'Must accept to continue'), }) .refine((data) => data.password === data.passwordAgain, { message: "Passwords don't match", path: ['passwordAgain'], }); export const signupFormSchema = emailStepSchema .and(infoStepSchema) .and(passwordStepSchema);
我的目标
- 从
signupFormSchema中提取所有可能的键 - 从每个步骤Schema(如
emailStepSchema、infoStepSchema、passwordStepSchema)中提取所有可能的键
目前我用Proxy实现了需求,但希望找到更不“hacky”的正规方法。
内容的提问来源于stack exchange,提问作者demux
相关产品推荐
相关产品推荐

