keep undefined values in .record()
Discussed in https://github.com/colinhacks/zod/discussions/2760
Originally posted by DanielHoffmann September 22, 2023
is there a way where I can type a record while keeping the keys that have undefined as a value?
For example, how could I set up this schema so this test passes?
import { z } from 'zod'
test('allow undefined values', () => {
const keySchema = z.string().startsWith('_')
const valueSchema = z.union([z.number(), z.undefined()])
const schema = z.record(keySchema, valueSchema)
expect(
Object.keys(
schema.parse({
_test: undefined,
}),
),
).toEqual(['_test'])
})
in this case the Object.keys(schema.parse(...)) is returning empty array
Could this be a bug? For example, this tests passes:
import { z } from 'zod'
test('allow undefined values', () => {
const schema = z.object({
a: z.union([z.number(), z.undefined()]),
})
expect(
Object.keys(
schema.parse({
a: undefined,
}),
),
).toEqual(['a'])
})
I would kind of expect .record() and .object() to behave the same in regards to the values
@DanielHoffmann, I have confirmed this code works the way you said and it does appear to be a bug. Thanks for reporting it.
Just going to throw a walk-around here for anyone else stumbles on this.
const undef = Symbol('undef');
const record = (val: z.ZodType) =>
z
.record(
z.string(),
z.preprocess((v) => (v == undefined ? undef : v), z.union([val, z.symbol(undef), z.undefined()])),
)
.transform((v) => {
for (const key in v) {
if (v[key] === undef) v[key] = undefined;
}
return v;
});
const x = record(z.number()).parse({ a: undefined });
Hi, @JacobWeisenburger. I'm Dosu, and I'm helping the Zod team manage their backlog. I'm marking this issue as stale.
Issue Summary:
- You reported a discrepancy in Zod's
.record()method, which removes keys withundefinedvalues, unlike.object(). - This behavior was confirmed as a bug after a report by DanielHoffmann.
- Louis-Tian provided a workaround using a custom symbol to preserve
undefinedvalues in.record(), which was well-received by the community.
Next Steps:
- Please let me know if this issue is still relevant to the latest version of Zod. If so, you can keep the discussion open by commenting on the issue.
- Otherwise, the issue will be automatically closed in 14 days.
Thank you for your understanding and contribution!
The issue is still relevant and moreover, it doesn't work for z.object as well:
z.object({ a: z.number().or(z.undefined()) }).parse({ a: undefined }); // expected: { a: undefined }, actual: {}
Hi, @JacobWeisenburger. I'm Dosu, and I'm helping the Zod team manage their backlog and am marking this issue as stale.
Issue Summary:
- You reported that
.record()removes keys withundefinedvalues during parsing, unlike.object()which keeps them. - This behavior was confirmed as a bug by DanielHoffmann.
- A workaround using a custom symbol to preserve
undefinedvalues in.record()was shared by Louis-Tian. - The issue remains unresolved and relevant, with similar behavior noted in
.object()by another user. - I have marked the issue as stale pending further input.
Next Steps:
- Please let me know if this issue is still relevant with the latest version of Zod by commenting here to keep the discussion open.
- Otherwise, this issue will be automatically closed in 7 days.
Thanks for your understanding and contribution!
Please reopen. The issue is still relevant.