使用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
相关产品推荐
相关产品推荐

