Skip to content

Consider compacting simple JSON Schema unions to type array #6047

@nwalters512

Description

@nwalters512

Consider this example:

import { z } from "zod";

const schema = z.object({
  value: z.string().nullable().default(null),
});

console.log(JSON.stringify(z.toJSONSchema(schema), null, 2));

In Zod 4, this omits something like the following (trimmed for brevity):

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "value": {
      "default": null,
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "value"
  ],
  "additionalProperties": false
}

For JSON Schema targets, the equivalent compact form is:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "value": {
      "default": null,
      "type": ["string", "null"]
    }
  },
  "required": [
    "value"
  ],
  "additionalProperties": false
}

Would you consider a conservative PR that would update toJSONSchema(...) to omit the more compact form when possible?

The proposed scope would be narrow:

  • only JSON Schema targets that support type arrays (draft-04, draft-07, draft-2020-12 - not openapi-3.0)
  • only anyOf where all branches have the form { type: ... }
  • no compaction when branches contain constraints, metadata, $ref, const, enum, etc.
  • no change to oneOf / exclusive unions

My motivation is that I have projects that generate JSON schemas and check them into version control, and we like having the simplest possible form of things for easier reading / review. We're looking to upgrade from v3 + https://github.com/StefanTerdell/zod-to-json-schema to v4. We love that Zod now has JSON Schema serialization built in, but are missing the compact form that zod-to-json-schema omits.

I'll acknowledge that we can do this in userspace as our own post-processing step, which we'll likely do if y'all don't want this in Zod itself. But I thought this could be something that others could appreciate too, so wanted to raise it for discussion. Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions