Skip to content

feat(zod): add useBrandedTypes option to append .brand() to generated schemas#3161

Merged
melloware merged 4 commits intoorval-labs:masterfrom
yamaaaaaa31:feat/zod-use-branded-types
Mar 28, 2026
Merged

feat(zod): add useBrandedTypes option to append .brand() to generated schemas#3161
melloware merged 4 commits intoorval-labs:masterfrom
yamaaaaaa31:feat/zod-use-branded-types

Conversation

@yamaaaaaa31
Copy link
Copy Markdown
Contributor

@yamaaaaaa31 yamaaaaaa31 commented Mar 26, 2026

Closes #3160

Description

Add a new override.zod.useBrandedTypes option. When enabled, every generated Zod schema gets a .brand() call appended, using the export variable name as the brand identifier.

Problem

Orval generates structurally typed Zod schemas. Two schemas with the same shape are fully interchangeable in TypeScript, so there is no compile-time error when you accidentally pass one where the other is expected. For example, a UserResponse and OrganizationResponse with identical fields can be silently swapped.

Solution

With useBrandedTypes: true, Orval appends .brand() to every generated Zod schema. This brings nominal type safety with zero runtime cost — Zod's .brand() is compile-time only.

// orval.config.ts
override: {
  zod: {
    useBrandedTypes: true,
  },
}

Generated output (Zod v3):

export const UserResponse = zod.object({
  id: zod.string(),
  name: zod.string(),
}).brand<"UserResponse">()

Generated output (Zod v4):

export const UserResponse = zod.object({
  id: zod.string(),
  name: zod.string(),
}).brand("UserResponse")

Changes

  • Add useBrandedTypes?: boolean to ZodOptions / NormalizedZodOptions in packages/core/src/types.ts
  • Default to false in config normalization (packages/orval/src/utils/options.ts)
  • Append .brand() in generateZodRoute export assembly (packages/zod/src/index.ts)
  • Add useBrandedTypes: false to test context defaults (packages/core/src/test-utils/context.ts)
  • Add 10 unit tests covering: v3/v4 syntax, array body/response, strict mode, generateEachHttpStatus, partial generation (packages/zod/src/zod.test.ts)
  • Add integration test config and snapshot (tests/configs/zod.config.ts, tests/__snapshots__/zod/branded-types/)
  • Add documentation (docs/content/docs/reference/configuration/output.mdx)

Note

This is different from #1222 / #2432 which focus on TypeScript type-level branded types (type X = string & { __brand }). This PR is specifically about Zod schema-level .brand() in the client: 'zod' output — a different code path.

Summary by CodeRabbit

  • New Features

    • Added useBrandedTypes option for Zod generation (default: false); enables optional branding on generated schemas and propagates into normalized and per-operation settings.
  • Documentation

    • Documented override.zod.useBrandedTypes and updated examples to show branded-schema behavior, including array-wrapper handling.
  • Tests

    • Added tests and snapshots covering branded output across Zod versions, array wrappers, and per-status exports.
  • Chores

    • Added a test configuration to generate branded-type outputs.

yamaaaaaa31 and others added 2 commits March 27, 2026 03:25
… schemas (orval-labs#3160)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 26, 2026 18:41
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ce927a6b-f3bc-47bb-bb2a-1de168017fcb

📥 Commits

Reviewing files that changed from the base of the PR and between aab28cd and 27c62c3.

📒 Files selected for processing (4)
  • packages/angular/src/http-client.test.ts
  • packages/angular/src/http-resource.test.ts
  • packages/mock/src/faker/getters/combine.test.ts
  • packages/solid-start/src/index.test.ts
✅ Files skipped from review due to trivial changes (3)
  • packages/solid-start/src/index.test.ts
  • packages/mock/src/faker/getters/combine.test.ts
  • packages/angular/src/http-client.test.ts

📝 Walkthrough

Walkthrough

Adds a useBrandedTypes Zod generation option, threads it through normalization and test defaults, and updates the Zod code generator and tests so generated schemas can optionally include Zod .brand(...) (v3/v4-aware); docs and snapshots updated.

Changes

Cohort / File(s) Summary
Types & Context
packages/core/src/types.ts, packages/core/src/test-utils/context.ts
Add useBrandedTypes to ZodOptions (optional) and NormalizedZodOptions (required boolean); add default useBrandedTypes: false in test context defaults.
Options Normalization
packages/orval/src/utils/options.ts
Propagate override.zod.useBrandedTypes into normalized options and include per-operation/per-tag Zod settings.
Zod Codegen & Tests
packages/zod/src/index.ts, packages/zod/src/zod.test.ts, tests/__snapshots__/zod/branded-types/branded-types.ts
Generate optional .brand(...) on exported Zod schemas when enabled (handles Zod v3 vs v4 syntax); tests added to assert branded vs non-branded outputs, array-wrapper behavior, per-status exports; add branded snapshot.
Docs & Test Configs
docs/content/docs/reference/configuration/output.mdx, tests/configs/zod.config.ts
Document override.zod.useBrandedTypes with example and add brandedTypes test configuration enabling branding.
Test Fixtures Updated
packages/angular/src/http-client.test.ts, packages/angular/src/http-resource.test.ts, packages/mock/src/faker/getters/combine.test.ts, packages/solid-start/src/index.test.ts
Include useBrandedTypes: false in various test output overrides / mock contexts to ensure the new flag is present in test defaults.

Sequence Diagram(s)

sequenceDiagram
  participant Config
  participant Normalizer
  participant Generator
  participant FileSystem

  rect rgba(200,230,201,0.5)
  Config->>Normalizer: load output.override.zod (useBrandedTypes?)
  end

  rect rgba(187,222,251,0.5)
  Normalizer->>Generator: provide normalized zod options (useBrandedTypes boolean)
  end

  rect rgba(255,224,178,0.5)
  Generator->>FileSystem: emit schema files (append .brand(...) when useBrandedTypes=true)
  FileSystem-->>Generator: write success
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Suggested reviewers

  • melloware

Poem

"🐰 I hopped through code and left a mark,

I stitched each schema with a tiny spark.
Now pets and users know who’s who,
Branded and tidy — hooray, that’s new! 🥕"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 PR title clearly summarizes the main change: adding a useBrandedTypes option to append .brand() to generated Zod schemas.
Linked Issues check ✅ Passed All requirements from issue #3160 are met: useBrandedTypes configuration option added, defaults to false, .brand() appended to generated schemas using schema names as identifiers, Zod v3/v4 syntax supported, zero runtime cost maintained.
Out of Scope Changes check ✅ Passed All changes directly support the useBrandedTypes feature: type definitions, option normalization, code generation, test utilities, documentation, and test coverage. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an opt-in Zod output feature to generate nominally-typed schemas by appending Zod’s .brand() to generated schema exports (v3 generic form vs v4 runtime-arg form), plus tests and docs.

Changes:

  • Introduces override.zod.useBrandedTypes in core option types and normalizes it to false by default.
  • Appends .brand() to generated route-level Zod schema exports, with v3/v4 syntax handling.
  • Adds unit + integration test coverage and documents the new option.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/configs/zod.config.ts Adds an integration test config entry enabling useBrandedTypes.
tests/snapshots/zod/branded-types/branded-types.ts New snapshot showing branded Zod schema exports.
packages/zod/src/zod.test.ts Adds unit tests covering branded output across v3/v4, arrays, strict, and per-status responses.
packages/zod/src/index.ts Implements .brand() appending during Zod route schema export assembly.
packages/orval/src/utils/options.ts Normalizes useBrandedTypes with default false (global + per-op/tag overrides).
packages/core/src/types.ts Adds useBrandedTypes to ZodOptions and NormalizedZodOptions.
packages/core/src/test-utils/context.ts Updates test context defaults to include useBrandedTypes: false.
docs/content/docs/reference/configuration/output.mdx Documents the new useBrandedTypes option and adds it to the example.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/zod/src/zod.test.ts`:
- Around line 6543-6545: The regex used in the assertion for
result.implementation has an extra `\)` which can cause false negatives when
matching branded item schemas; update the pattern in the expect call (the line
referencing result.implementation) to remove the extra paren and allow optional
whitespace before the `.brand`, e.g. change the regex to match /TestResponseItem
= zod\.object\([^)]*\)\s*\.brand/ so branded schemas are reliably detected.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 49f0a1d3-b564-449a-bb42-07d8bb34c3fc

📥 Commits

Reviewing files that changed from the base of the PR and between c1f128d and 32fea2c.

📒 Files selected for processing (8)
  • docs/content/docs/reference/configuration/output.mdx
  • packages/core/src/test-utils/context.ts
  • packages/core/src/types.ts
  • packages/orval/src/utils/options.ts
  • packages/zod/src/index.ts
  • packages/zod/src/zod.test.ts
  • tests/__snapshots__/zod/branded-types/branded-types.ts
  • tests/configs/zod.config.ts

- Fix regex false negative in array response item brand assertion
- Document that *Item helper schemas are intentionally not branded

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@melloware melloware added the zod Zod related issue label Mar 26, 2026
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@melloware melloware requested a review from snebjorn March 27, 2026 11:06
Copy link
Copy Markdown
Contributor

@snebjorn snebjorn left a comment

Choose a reason for hiding this comment

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

LGTM

@melloware melloware merged commit 9483f4c into orval-labs:master Mar 28, 2026
4 checks passed
@yamaaaaaa31 yamaaaaaa31 deleted the feat/zod-use-branded-types branch March 28, 2026 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

zod Zod related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(zod):Support Zod .brand() generation using OpenAPI $ref schema names

4 participants