zod parsing bug
This is my complex object zod definition
import { z } from 'zod'
import { FieldDataType } from '../../enums/filed-data-type'
import { FieldDataChromoShow } from '../../enums/field-data-chromo-show'
import { EventCategory } from '../../enums/event-category'
const genericAlarmEventFieldDataPayloadSchema = z.object({
NtpError: z.coerce.boolean(),
DeviceNotFound: z.coerce.boolean(),
MasterNotFound: z.coerce.boolean(),
})
const hammamAlarmEventFieldDataPayloadSchema = z.object({
BoilerFullTooSoon: z.coerce.boolean(),
EvLoad: z.coerce.boolean(),
EvDrain: z.coerce.boolean(),
VinBus: z.coerce.boolean(),
Ntc: z.coerce.boolean(),
Fan: z.coerce.boolean(),
Slave: z.coerce.boolean(),
HeaterFail: z.coerce.boolean(),
Capacitive: z.coerce.boolean(),
CabTemp: z.coerce.boolean(),
Door: z.coerce.boolean(),
})
const saunaAlarmEventFieldDataPayloadSchema = z.object({
StoveTemp: z.coerce.boolean(),
VinBus: z.coerce.boolean(),
Ntc: z.coerce.boolean(),
ThermalProtection: z.coerce.boolean(),
CabTemp: z.coerce.boolean(),
Potentiometer: z.coerce.boolean(),
})
const eccIotInfoSchema = z.object({
Id: z.string(),
Ts: z.coerce.date(),
Type: z.nativeEnum(FieldDataType),
HostId: z.string(),
Payload: saunaAlarmEventFieldDataPayloadSchema
.or(genericAlarmEventFieldDataPayloadSchema)
.or(hammamAlarmEventFieldDataPayloadSchema),
})
export const rawEventFieldDataSchema = z.object({
App: z.string(),
Cat: z.nativeEnum(EventCategory),
EccIOTEvent: eccIotInfoSchema,
})
export type RawEventFieldData = z.infer<typeof rawEventFieldDataSchema>
When I use this definition, in my code, zod parses it wrongly
const body = {
"App": "EccIOT",
"Cat": "Event",
"EccIOTEvent":
{
"Id": "ecc-D8E39657C",
"Ts": "2020/02/28 10:14:10",
"Type":"HAlarm",
"HostId":"0A229332",
"Payload":{
"BoilerFullTooSoon" : 0,
"EvLoad": 0,
"EvDrain": 0,
"VinBus": 0,
"Ntc": 0,
"Fan": 0,
"Slave": 0,
"HeaterFail": 0,
"Capacitive": 0,
"CabTemp": 0,
"Door": 0
}
}
}
const rawData: RawEventFieldData = rawEventFieldDataSchema.parse(body)
console.log(rawData);
In the console I found this:
{
App: 'EccIOT',
Cat: 'Event',
EccIOTEvent: {
Id: 'ecc-D8E39657C',
Ts: 2020-02-28T10:14:10.000Z,
Type: 'HAlarm',
HostId: '0A229332',
Payload: {
StoveTemp: false,
VinBus: false,
Ntc: false,
ThermalProtection: false,
CabTemp: false,
Potentiometer: false
}
}
}
What am I doing wrong ?
Zod version is 3.23.8
Compiler options already includes
"compilerOptions": { "strict": true }
Hi! Not totally sure what is causing the problem with the union type, but I think a discriminated union might work instead, e.g.:
const hammamAlarmEventFieldDataPayloadSchema = z.object({
type: z.literal("hamman"),
BoilerFullTooSoon: z.coerce.boolean(),
EvLoad: z.coerce.boolean(),
EvDrain: z.coerce.boolean(),
VinBus: z.coerce.boolean(),
Ntc: z.coerce.boolean(),
Fan: z.coerce.boolean(),
Slave: z.coerce.boolean(),
HeaterFail: z.coerce.boolean(),
Capacitive: z.coerce.boolean(),
CabTemp: z.coerce.boolean(),
Door: z.coerce.boolean(),
});
const saunaAlarmEventFieldDataPayloadSchema = z.object({
type: z.literal("sauna"),
StoveTemp: z.coerce.boolean(),
VinBus: z.coerce.boolean(),
Ntc: z.coerce.boolean(),
ThermalProtection: z.coerce.boolean(),
CabTemp: z.coerce.boolean(),
Potentiometer: z.coerce.boolean(),
});
const eccIotInfoSchema = z.object({
Id: z.string(),
Ts: z.coerce.date(),
Type: z.nativeEnum(FieldDataType),
HostId: z.string(),
Payload: z.discriminatedUnion("type", [
saunaAlarmEventFieldDataPayloadSchema,
genericAlarmEventFieldDataPayloadSchema,
hammamAlarmEventFieldDataPayloadSchema,
]),
});
export const rawEventFieldDataSchema = z.object({
App: z.string(),
Cat: z.nativeEnum(EventCategory),
EccIOTEvent: eccIotInfoSchema,
});
export type RawEventFieldData = z.infer<typeof rawEventFieldDataSchema>;
const body = {
App: "EccIOT",
Cat: "Event",
EccIOTEvent: {
Id: "ecc-D8E39657C",
Ts: "2020/02/28 10:14:10",
Type: "HAlarm",
HostId: "0A229332",
Payload: {
type: "hamman",
BoilerFullTooSoon: 0,
EvLoad: 0,
EvDrain: 0,
VinBus: 0,
Ntc: 0,
Fan: 0,
Slave: 0,
HeaterFail: 0,
Capacitive: 0,
CabTemp: 0,
Door: 0,
},
},
};
const rawData: RawEventFieldData = rawEventFieldDataSchema.parse(body);
console.log(rawData);
I installed zod-to-ts to diagnose the problem.
Actually, having used coerce for the boolean, every missing property is mapped as a boolean false. and the ts types are all optionals (there is a ? added after the field name in every coerced field)
We resolved using discriminatedUnion, thanks for the tip!
I kindly suggest to add a tip in the documentation about this behaviour.
Hi, @realtebo. I'm Dosu, and I'm helping the Zod team manage their backlog. I'm marking this issue as stale.
Issue Summary
- You reported an issue with a Zod schema not parsing a complex object correctly, especially with nested objects and enums.
- @eswarty suggested using a discriminated union, providing a detailed code example.
- You confirmed that the discriminated union approach resolved the issue and suggested a documentation update regarding
coercefor booleans.
Next Steps
- Is this issue still relevant to the latest version of the Zod repository? If so, please comment to keep the discussion open.
- Otherwise, this issue will be automatically closed in 7 days.
Thank you for your understanding and contribution!