You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 18:05:57