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

使用Zod Union Schema时VS Code类型校验报错问题咨询

问题:Zod联合类型在TypeScript中无法自动收窄导致属性报错

我在VS Code中使用Zod Schema校验API调用结果,该API会返回两种结构之一:一种是包含Appointment字段的对象,另一种是包含Error字段的对象。但在解析结果的代码中,VS Code会针对.Appointment或.Error属性报错,提示该属性可能不存在。

最小复现示例

import z from 'zod';
import { useState } from "react";

const appointmentSchema = z
  .object({
    Appointment: z.string(),
  })
  .or(
    z.object({
      Error: z.object({
        Message: z.string(),
      }),
    }),
);

type AppointmentSchema = z.infer<typeof appointmentSchema>;

const twoObjects = [
  {
    Appointment: "test"
  },
  {
    Error: {
    Message: "test",
    }
  },
];

const random = async ():Promise<AppointmentSchema> => {
  const rando = Math.floor(Math.random() * twoObjects.length);
  return twoObjects[rando];
}

export default function View() {

  const [appointment, setAppointment] = useState<AppointmentSchema>();

  return (
    <div>
      <h1>Testing!!!!</h1>
      <button
        onClick={async () => {
          const randObject = await random();
          setAppointment(randObject);
        }
      }>Random Select</button>
      {appointment && appointment.Appointment && <div>Appointment: {appointment.Appointment}</div>}
    </div>
  );
}

尝试过的方法(未解决)

借助TypeScript类型收窄相关内容,尝试手动收窄类型:

{appointment && typeof appointment === 'object' && 'Appointment' in appointment && (
  <div>Appointment: {appointment.Appointment}</div>
)}

还尝试使用单独的Zod对象:

const appointmentOnlySchema = z.object({
  Appointment: z.string(),
});
type AppointmentOnlySchema = z.infer<typeof appointmentOnlySchema>;

将判断条件替换为类型检查或appointmentOnlySchema.safeParse(appointment).success,但编辑器仍提示对象可能不包含指定属性。


解决方案

这不是编辑器的问题,是TypeScript对联合类型的收窄规则限制导致的。TypeScript无法仅通过'字段名' in 对象的判断自动将联合类型收窄到具体分支,需要明确的类型守卫来辅助。

1. 使用Zod内置的类型守卫

Zod的Schema提供了is方法,可生成能被TypeScript识别的类型守卫函数:

// 生成类型守卫函数
const isAppointment = appointmentSchema.pick({ Appointment: true }).is;
const isError = appointmentSchema.pick({ Error: true }).is;

// 在组件中使用
export default function View() {
  const [appointment, setAppointment] = useState<AppointmentSchema>();

  return (
    <div>
      <h1>Testing!!!!</h1>
      <button
        onClick={async () => {
          const randObject = await random();
          setAppointment(randObject);
        }}
      >Random Select</button>
      {appointment && isAppointment(appointment) && (
        <div>Appointment: {appointment.Appointment}</div>
      )}
      {appointment && isError(appointment) && (
        <div>Error: {appointment.Error.Message}</div>
      )}
    </div>
  );
}

2. 自定义类型守卫函数

如果不想依赖Zod的内置方法,也可以手动编写类型守卫:

// 自定义类型守卫
function isAppointment(obj: AppointmentSchema): obj is { Appointment: string } {
  return 'Appointment' in obj;
}

function isError(obj: AppointmentSchema): obj is { Error: { Message: string } } {
  return 'Error' in obj;
}

// 组件中使用方式和上述一致

3. 类型断言(不推荐)

如果能100%确定当前对象的类型,可使用类型断言跳过检查,但这会失去TypeScript的类型安全保障:

{appointment && (
  <div>Appointment: {(appointment as { Appointment: string }).Appointment}</div>
)}

内容的提问来源于stack exchange,提问作者MikeiLL

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 09:18:42