Skip to content

feat: allow zod discriminatedUnion generation - #3698

Merged
melloware merged 1 commit into
orval-labs:masterfrom
titivermeesch:feat/zod-discriminated-union-safe
Jul 10, 2026
Merged

feat: allow zod discriminatedUnion generation#3698
melloware merged 1 commit into
orval-labs:masterfrom
titivermeesch:feat/zod-discriminated-union-safe

Conversation

@titivermeesch

@titivermeesch titivermeesch commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds an opt-in override.zod.generateDiscriminatedUnion flag that emits z.discriminatedUnion(key, [...]) for a oneOf/anyOf carrying an OpenAPI discriminator, instead of a plain z.union([...]).

This reintroduces #1907 (reverted in #2118 due to #2085) but avoids the crash
that forced the revert.

Why the previous attempt was reverted

z.discriminatedUnion only accepts object options. An inheritance branch (allOf) was rendered as z.object().and(...) which zod rejects at construction, crashing the generated module (#2085).

Approach

  • Go/no-go at generation time: emit a discriminated union only when every branch can be represented as an object carrying a literal (const/enum) discriminator.
  • allOf branches are flattened into a single object (reusing the existing strict-mode object-merge) so they stay valid options
  • Safe fallback to z.union whenever a branch can't be an object: nested unions, non-object members, non-literal discriminators, or (with generateReusableSchemas) a branch that $refs an allOf schema. The generator never emits code that throws at construction.
  • Opt-in, default false

Summary by CodeRabbit

  • New Features

    • Added an opt-in generateDiscriminatedUnion setting for Zod output.
    • When enabled and compatible, emits zod.discriminatedUnion for discriminator-based oneOf/anyOf, with safe fallback to zod.union.
    • Supports Zod v3, Zod v4, and the Mini variant.
  • Documentation

    • Updated override.zod docs with the new option and clarified that operation/tag-level usage is ignored.
  • Tests

    • Expanded discriminated-union coverage (including inheritance, reusable schemas, and nullability).
    • Updated generator test defaults to explicitly disable the option where needed.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0c02dd9c-62a1-4ead-b26e-3d720d4a3872

📥 Commits

Reviewing files that changed from the base of the PR and between 5d97b1d and b136d52.

📒 Files selected for processing (11)
  • docs/content/docs/guides/zod.mdx
  • docs/content/docs/reference/configuration/output.mdx
  • packages/angular/src/http-client.test.ts
  • packages/angular/src/http-resource.test.ts
  • packages/core/src/test-utils/context.ts
  • packages/core/src/types.ts
  • packages/mock/src/faker/getters/combine.test.ts
  • packages/orval/src/utils/options.ts
  • packages/solid-start/src/index.test.ts
  • packages/zod/src/index.ts
  • packages/zod/src/zod.test.ts
✅ Files skipped from review due to trivial changes (5)
  • packages/mock/src/faker/getters/combine.test.ts
  • packages/angular/src/http-resource.test.ts
  • packages/core/src/test-utils/context.ts
  • docs/content/docs/guides/zod.mdx
  • docs/content/docs/reference/configuration/output.mdx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/solid-start/src/index.test.ts
  • packages/angular/src/http-client.test.ts
  • packages/core/src/types.ts
  • packages/orval/src/utils/options.ts
  • packages/zod/src/index.ts

📝 Walkthrough

Walkthrough

Adds an opt-in generateDiscriminatedUnion Zod option. Safe OpenAPI discriminator unions are rendered as zod.discriminatedUnion across classic and mini variants, with plain-union fallbacks, configuration normalization, tests, and documentation.

Changes

Zod discriminated union generation

Layer / File(s) Summary
Configuration contract and normalization
packages/core/src/types.ts, packages/orval/src/utils/options.ts, packages/*/src/*.test.ts
Adds the opt-in option, defaults it to false, normalizes it globally, and ignores it for operation and tag overrides.
Discriminator detection and union wiring
packages/zod/src/index.ts
Validates discriminator branches and carries eligible discriminator metadata through union generation.
Classic and mini union rendering
packages/zod/src/index.ts
Emits discriminated unions for supported branches, merges strict allOf objects, and retains regular unions when branches are unsuitable.
Behavior coverage and configuration documentation
packages/zod/src/zod.test.ts, docs/content/docs/guides/zod.mdx, docs/content/docs/reference/configuration/output.mdx
Covers supported and fallback cases across variants and versions, and documents the new global setting.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Config
  participant ZodGenerator
  participant BranchValidator
  participant ZodRenderer
  Config->>ZodGenerator: enable generateDiscriminatedUnion
  ZodGenerator->>BranchValidator: validate discriminator-bearing branches
  BranchValidator-->>ZodGenerator: return eligible or fallback result
  ZodGenerator->>ZodRenderer: pass discriminator-aware union marker
  ZodRenderer-->>Config: render discriminatedUnion or union
Loading

Possibly related PRs

Suggested labels: zod

Suggested reviewers: melloware, z4o4z, snebjorn

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding opt-in Zod discriminatedUnion generation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/zod/src/index.ts`:
- Around line 241-252: Escape discriminator property names before storing them
in the internal marker, using a collision-safe encoding that safely handles the
discriminator marker itself, and decode that payload in
decodeDiscriminatorSeparator. Update the generated TypeScript rendering paths at
the referenced discriminator handling sites to emit the decoded property via
JSON.stringify or equivalent string-literal escaping, including names containing
apostrophes or marker text; keep encode/decode behavior consistent across all
call sites.
- Around line 1475-1494: When flattening allOf schemas, update the helpers that
build mergedProperties to preserve constraints for duplicate property keys
instead of allowing Object.assign to overwrite earlier definitions. Merge
overlapping ZodValidationSchemaDefinition values using the existing
schema-combination approach, or skip flattening when property sets overlap, and
apply the same behavior consistently to both helpers handling
object/strictObject functions.
- Around line 619-631: Update the discriminated-union eligibility logic around
discriminatorProperty and isDiscriminatableMember to collect all discriminator
values from every schema branch, detect duplicates across branches (including
const and enum values), and require the combined values to be unique; otherwise
disable discriminatedUnion generation so the code falls back to zod.union.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b52bbaf5-f239-4081-b445-523c462b56f5

📥 Commits

Reviewing files that changed from the base of the PR and between 1134ba5 and 5d97b1d.

📒 Files selected for processing (11)
  • docs/content/docs/guides/zod.mdx
  • docs/content/docs/reference/configuration/output.mdx
  • packages/angular/src/http-client.test.ts
  • packages/angular/src/http-resource.test.ts
  • packages/core/src/test-utils/context.ts
  • packages/core/src/types.ts
  • packages/mock/src/faker/getters/combine.test.ts
  • packages/orval/src/utils/options.ts
  • packages/solid-start/src/index.test.ts
  • packages/zod/src/index.ts
  • packages/zod/src/zod.test.ts

Comment thread packages/zod/src/index.ts
Comment thread packages/zod/src/index.ts Outdated
Comment thread packages/zod/src/index.ts
@pkg-pr-new

pkg-pr-new Bot commented Jul 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@orval/angular

bun add https://pkg.pr.new/@orval/angular@b136d52

@orval/axios

bun add https://pkg.pr.new/@orval/axios@b136d52

@orval/core

bun add https://pkg.pr.new/@orval/core@b136d52

@orval/effect

bun add https://pkg.pr.new/@orval/effect@b136d52

@orval/fetch

bun add https://pkg.pr.new/@orval/fetch@b136d52

@orval/hono

bun add https://pkg.pr.new/@orval/hono@b136d52

@orval/mcp

bun add https://pkg.pr.new/@orval/mcp@b136d52

@orval/mock

bun add https://pkg.pr.new/@orval/mock@b136d52

orval

bun add https://pkg.pr.new/orval@b136d52

@orval/query

bun add https://pkg.pr.new/@orval/query@b136d52

@orval/solid-start

bun add https://pkg.pr.new/@orval/solid-start@b136d52

@orval/swr

bun add https://pkg.pr.new/@orval/swr@b136d52

@orval/zod

bun add https://pkg.pr.new/@orval/zod@b136d52

commit: b136d52

@titivermeesch
titivermeesch force-pushed the feat/zod-discriminated-union-safe branch from 5d97b1d to b136d52 Compare July 10, 2026 11:58
@titivermeesch

Copy link
Copy Markdown
Contributor Author

@melloware @Georgegriff tagging you since you were involved a lot in the previous discussions around the topic.

@melloware

Copy link
Copy Markdown
Collaborator

This seems reasonable to me!

@melloware melloware added the zod Zod schema client related issue label Jul 10, 2026
@melloware melloware added this to the 8.21.0 milestone Jul 10, 2026
@melloware
melloware merged commit aa1049f into orval-labs:master Jul 10, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

zod Zod schema client related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants