Skip to content

Zod 4 support: fix VUnion<… …[]> handling in convexToZod#872

Open
Nicolapps wants to merge 3 commits intomainfrom
nicolas/fix-weird-array-types
Open

Zod 4 support: fix VUnion<… …[]> handling in convexToZod#872
Nicolapps wants to merge 3 commits intomainfrom
nicolas/fix-weird-array-types

Conversation

@Nicolapps
Copy link
Copy Markdown
Member

@Nicolapps Nicolapps commented Dec 5, 2025

Fixes #861

Summary by CodeRabbit

  • Tests

    • Added regression tests for literals helper and enum spreading into unions.
  • Improvements

    • Enhanced multi-member union type handling and conversion accuracy.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 5, 2025

📝 Walkthrough

Walkthrough

The changes extend the convexToZod converter to properly handle multi-member union validators (VUnion with 2+ members), addressing incompatibility with the literals helper. Two regression tests validate correct behavior.

Changes

Cohort / File(s) Summary
Test Coverage
packages/convex-helpers/server/zod4.convextozod.test.ts
Added two regression tests validating that literals(...) helper and spreading enums into v.union correctly map to Zod union types with corresponding literal values.
Union Handling
packages/convex-helpers/server/zod4.ts
Extended ZodFromValidatorBase and ZodFromStringValidator type signatures to detect and handle multi-member VUnion (2+ validators), properly mapping them to ZodUnion with first two members plus a rest array for remaining validators.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • ianmacartney

Poem

🐰 A union of literals, now working with grace,
The helpers and zod types can finally embrace,
Multi-member unions no longer a fright,
With proper type mapping, everything's right! ✨

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: fixing VUnion handling in convexToZod for Zod 4 support.
Linked Issues check ✅ Passed The PR's code changes directly address issue #861 by extending VUnion handling in convexToZod to support multi-member unions, enabling literals() helper to work correctly.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing VUnion handling in convexToZod as specified in issue #861; no unrelated changes are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch nicolas/fix-weird-array-types

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.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Dec 5, 2025

Open in StackBlitz

npm i https://pkg.pr.new/get-convex/convex-helpers@872

commit: 085c812

@Nicolapps Nicolapps marked this pull request as ready for review February 17, 2026 00:19
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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/convex-helpers/server/zod4.ts (2)

943-964: ⚠️ Potential issue | 🔴 Critical

Same readonly constraint issue exists in ZodFromStringValidator.

This branch mirrors the one in ZodFromValidatorBase and will have the same type error once the compiler reaches it. Apply the same readonly removal fix here.

Proposed fix
              : V extends VUnion<
                    any,
-                   infer Members extends readonly [
+                   infer Members extends [
                      GenericValidator,
                      GenericValidator,
                      ...GenericValidator[],
                    ],
                    any,
                    any
                  >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/convex-helpers/server/zod4.ts` around lines 943 - 964, The
ZodFromStringValidator branch carries the same readonly tuple constraint that
causes the type error; open the ZodFromStringValidator declaration and remove
the readonly modifiers from the tuple/generic constraint (make [] |
[Record<string, any>] mutable, matching the fix applied to ZodFromValidatorBase)
so the ArgsForHandlerType-style merging works; ensure the tuple/generic
signatures and any inferred tuple types in ZodFromStringValidator mirror the
non-readonly form used in ZodFromValidatorBase (no other behavioral changes).

880-905: ⚠️ Potential issue | 🔴 Critical

Fix type constraint errors in VUnion type mappings at lines 1880–1910 and 1930–1965.

The TypeScript type checker reports indexing errors in two locations where Members (a tuple type) is processed with a filtered mapped type:

packages/convex-helpers/server/zod4.ts(1873,37): error TS2574: A rest element type must be an array type.
packages/convex-helpers/server/zod4.ts(1894,42): error TS2536: Type 'Exclude<keyof Members, "0" | "1">' cannot be used to index type '{...}'.
packages/convex-helpers/server/zod4.ts(1938,21): error TS2574: A rest element type must be an array type.
packages/convex-helpers/server/zod4.ts(1957,26): error TS2536: Type 'Exclude<keyof Members, "0" | "1">' cannot be used to index type '{...}'.

The issue is with the mapped-type approach that filters out indices "0" and "1":

...{
  [K in keyof Members as K extends "0" | "1" ? never : K]: ZodValidatorFromConvex<Members[K]>;
}[Exclude<keyof Members, "0" | "1">][]

This pattern produces an object type, not an array. The mapped type with an as clause filters keys but creates an incompatible type for the rest spread operator and subsequent indexing. Consider using a recursive utility type or conditional-type approach (similar to ConvexUnionValidatorFromZodMembers) that directly recurses over remaining tuple elements instead of relying on mapped-type filtering.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/convex-helpers/server/zod4.ts` around lines 880 - 905, The VUnion
type mapping is producing an object type (and causing TS2574/TS2536 errors)
because the mapped-type trick that filters keys ("0" | "1") yields an object,
not an array; replace that mapped-type with a recursive conditional tuple
utility that walks the Members tuple and builds an array of
ZodValidatorFromConvex<...> for the remaining elements (similar to how
ConvexUnionValidatorFromZodMembers recurses), and then use that recursive result
for indexing/rest operations; update the type aliases that reference Members and
ZodValidatorFromConvex (and any uses in VUnion) to use the new recursive utility
so the compiler sees an actual array/tuple type rather than an object.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/convex-helpers/server/zod4.ts`:
- Around line 943-964: The ZodFromStringValidator branch carries the same
readonly tuple constraint that causes the type error; open the
ZodFromStringValidator declaration and remove the readonly modifiers from the
tuple/generic constraint (make [] | [Record<string, any>] mutable, matching the
fix applied to ZodFromValidatorBase) so the ArgsForHandlerType-style merging
works; ensure the tuple/generic signatures and any inferred tuple types in
ZodFromStringValidator mirror the non-readonly form used in ZodFromValidatorBase
(no other behavioral changes).
- Around line 880-905: The VUnion type mapping is producing an object type (and
causing TS2574/TS2536 errors) because the mapped-type trick that filters keys
("0" | "1") yields an object, not an array; replace that mapped-type with a
recursive conditional tuple utility that walks the Members tuple and builds an
array of ZodValidatorFromConvex<...> for the remaining elements (similar to how
ConvexUnionValidatorFromZodMembers recurses), and then use that recursive result
for indexing/rest operations; update the type aliases that reference Members and
ZodValidatorFromConvex (and any uses in VUnion) to use the new recursive utility
so the compiler sees an actual array/tuple type rather than an object.

@Nicolapps Nicolapps force-pushed the nicolas/fix-weird-array-types branch from eed47d3 to 085c812 Compare February 17, 2026 00:32
Copy link
Copy Markdown
Member

@ianmacartney ianmacartney left a comment

Choose a reason for hiding this comment

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

looks like it's only tests right now, but the tests look good, feel free to land once they're passing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incompatibility between literals helper and zod

2 participants