-
-
Notifications
You must be signed in to change notification settings - Fork 94
Description
Description
Take for example z.set(z.number()).default(new Set())
.
This pre-populates form data with an empty object ({}
) instead of a Set (Set(0) {}
) as its default value.
Likewise, z.set(z.number()).default(new Set([1, 2, 3]))
also generates {}
instead of the expected Set(3) { 1, 2, 3 }
.
If relying on the form data having a proper Set
, possibly with pre-filled values, in its initial state (i.e. using default values from zod), this will break things that access any Set
specific members, such as Set.has()
.
This is a regression from using superforms with zod v3. It works fine there.
Root cause is the new toJSONSchema function used in the zod4 adapter. Due to the override function in superforms using the unrepresentable: 'any'
option any unrepresentable object will become an empty object ({}
).
Comparison of v3 and v4 generated JSON Schema
schema.ts
:
const myDefaultSet = z.object({
set: z.set(z.number()).default(new Set([1, 2, 3])),
});
v3
{
type: 'object',
properties: {
set: {
type: 'array',
uniqueItems: true,
items: { type: 'number' },
default: Set(3) { 1, 2, 3 }
}
},
additionalProperties: false,
'$schema': 'http://json-schema.org/draft-07/schema#'
}
v4 with superforms' default override options
{
'$schema': 'https://json-schema.org/draft/2020-12/schema',
type: 'object',
properties: { set: { default: {} } },
required: [ 'set' ],
additionalProperties: false
}
Thoughts
I looked into providing a fix myself by modifying the override
function. However, after studying zod's v4 to-json-schema.ts I concluded this will get messy quick due to the needed state tracking and recursion.
The reason is that z.default()
in schema generation is parent to z.set(z.number)
, and the z.set(z.number)
def
does not seem to have any indication of that.
This means, that while processing the z.default
, which has the desired defaultValue (in def.defaultValue
) that we need to put into the custom JSON Schema, we can see that there is a def.innerType
of z.set(z.number())
but we cannot alter that node (as it is actually processed first and already in the schema at that point). That default value is unfortunately also not found in the child z.set(z.number)
def
.
In summary, this issue will affect all non-JSONSchema-representable types that are used with default()
as well, not just Set()
.