如何在Zod的refine验证函数中访问DiscriminatedUnion特定分支属性?
解决Zod中discriminatedUnion分支属性在refine中无法访问的TypeScript类型问题
你的问题核心是:TypeScript无法自动关联外层contract.contractType和specificationContrat.contractType的一致性,导致即使判断了contract.contractType === 'DDD',也无法自动收窄specificationContrat的类型到对应分支,从而无法访问endDate。
这里提供两种可行的解决方法:
方法一:自定义类型守卫(推荐,类型安全且自动补全友好)
写一个类型守卫函数,明确告诉TypeScript当contractType为'DDD'时,specificationContrat包含endDate属性:
// 定义类型守卫函数 function isDddContractSpec( spec: z.infer<typeof formSchema>['contract']['specificationContrat'] ): spec is { contractType: 'DDD'; endDate: string } { return spec.contractType === 'DDD'; } // 调整refine逻辑 export const formSchema = z.object({ // 其他字段... contract: z.object({ startDate: z.string().describe('text'), contractType: z.enum(['III', 'DDD']).describe('select'), specificationContrat: z.discriminatedUnion('contractType', [ z.object({ contractType: z.literal('III'), ...someSchema.shape, }), z.object({ contractType: z.literal('DDD'), endDate: z.string().describe('text'), ...someSchema.shape, }) ]), }) .refine( (contract) => { return contract.contractType === 'III' || (isDddContractSpec(contract.specificationContrat) && new Date(contract.specificationContrat.endDate) > new Date(contract.startDate)); }, '"endDate"必须晚于"startDate"' ) });
方法二:手动类型断言
如果不想额外写类型守卫,可以直接在判断后对specificationContrat进行类型断言:
export const formSchema = z.object({ // 其他字段... contract: z.object({ startDate: z.string().describe('text'), contractType: z.enum(['III', 'DDD']).describe('select'), specificationContrat: z.discriminatedUnion('contractType', [ z.object({ contractType: z.literal('III'), ...someSchema.shape, }), z.object({ contractType: z.literal('DDD'), endDate: z.string().describe('text'), ...someSchema.shape, }) ]), }) .refine( (contract) => { if (contract.contractType === 'III') return true; // 断言为DDD分支的类型 const dddSpec = contract.specificationContrat as { contractType: 'DDD'; endDate: string }; return new Date(dddSpec.endDate) > new Date(contract.startDate); }, '"endDate"必须晚于"startDate"' ) });
补充说明
原代码报错的原因是:TypeScript无法推断出contract.contractType和specificationContrat.contractType是强关联的——虽然业务逻辑中两者一致,但从类型层面它们是独立的属性。通过类型守卫或断言,我们可以手动告诉TypeScript两者的关联关系,从而正确访问分支属性。
内容的提问来源于stack exchange,提问作者Stphane
相关产品推荐
相关产品推荐

