Skip to content

fix(orm): export UncheckedCreateInput/CheckedCreateInput and add XOR to UpdateInput#2627

Merged
ymc9 merged 2 commits into
devfrom
fix/issue-2567-unchecked-create-update-input
Apr 29, 2026
Merged

fix(orm): export UncheckedCreateInput/CheckedCreateInput and add XOR to UpdateInput#2627
ymc9 merged 2 commits into
devfrom
fix/issue-2567-unchecked-create-update-input

Conversation

@ymc9
Copy link
Copy Markdown
Member

@ymc9 ymc9 commented Apr 29, 2026

Fixes #2567

Problem

Spreading Partial<PostCreateArgs['data']> alongside an explicit FK field caused a TypeScript error because CreateArgs['data'] is an XOR union — its exclusive keys become ?: never in each branch, and Partial<XOR<A,B>> collapses that to ?: T | undefined which is not assignable to ?: never.

Solution

Export the two halves of the XOR as standalone public types (mirroring Prisma 6):

  • UncheckedCreateInput — scalars + FK fields + non-owned relations (no relation objects)
  • CheckedCreateInput — scalars + relation objects (no FK fields)

Users can now write Partial<PostUncheckedCreateInput> for builder/factory patterns while the XOR on CreateArgs['data'] still enforces mutual exclusion at the call site.

The same pattern is applied to update operations:

  • UncheckedUpdateInput — scalars + FK fields + non-owned relations
  • CheckedUpdateInput — scalars + relation objects

UpdateInput now also uses XOR (previously it did not enforce mutual exclusion between FK fields and relation objects).

All four new types are emitted in input.ts by the TypeScript schema generator.

Changes

  • packages/orm/src/client/crud-types.ts — export four new public types; wire UpdateInput through XOR; add UpdateNonOwnedRelationInput so non-owned relations (e.g. Post.comments) appear in both XOR branches
  • packages/sdk/src/ts-schema-generator.ts — add the four new types to the inputTypes list so they are emitted in input.ts
  • tests/regression/test/issue-2567/ — regression test for the original bug
  • tests/e2e/orm/client-api/checked-unchecked.test.ts — runtime tests verifying mixed FK+relation payloads are rejected
  • tests/e2e/orm/client-api/checked-unchecked.test-d.ts — type-level tests verifying XOR enforcement and correct shapes of the new exported types

Test plan

  • tests/regression/test/issue-2567/regression.test.ts passes — Partial<PostUncheckedCreateInput> spread works at runtime and compile time
  • tests/e2e/orm/client-api/checked-unchecked.test.ts passes — runtime rejects mixed FK+relation payloads in create and update
  • tests/e2e/orm/client-api/checked-unchecked.test-d.ts passes — type-level XOR enforcement verified for create and update
  • tests/e2e/orm/client-api/update.test.ts passes — no regressions in existing update tests

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Introduced explicit unchecked vs checked create/update input types to separate foreign-key scalar payloads from relation-object payloads.
    • Update input now enforces an XOR between unchecked and checked variants; updateMany supports FK-only payloads.
  • Tests

    • Added TypeScript and runtime e2e/regression tests verifying distinct checked/unchecked shapes and enforcement that FK fields and relation objects cannot be mixed.

…to UpdateInput (#2567)

- Export `UncheckedCreateInput` and `CheckedCreateInput` as public types so users
  can use `Partial<ModelUncheckedCreateInput>` in builder/factory patterns without
  hitting the XOR type error when spreading alongside an explicit FK field
- Apply the same XOR pattern to `UpdateInput` for consistency with create
- Export `UncheckedUpdateInput` and `CheckedUpdateInput` as public types
- Add non-owned relation fields to `UncheckedUpdateInput` (mirrors create behavior
  where non-owned relations are valid in both the unchecked and checked branches)
- Emit the four new input types in ts-schema-generator's input.ts output
- Add regression test for issue #2567
- Add e2e tests for runtime and type-level XOR enforcement on create/update

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

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: d62958d6-a39c-4261-8e8a-04c9f65b3b3a

📥 Commits

Reviewing files that changed from the base of the PR and between 96ac1aa and 7883850.

📒 Files selected for processing (3)
  • packages/orm/src/client/crud-types.ts
  • tests/e2e/orm/client-api/checked-unchecked.test-d.ts
  • tests/e2e/orm/client-api/update-many.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/e2e/orm/client-api/checked-unchecked.test-d.ts

📝 Walkthrough

Walkthrough

The PR splits Create/Update input types into exported "unchecked" (FK-scalar) and "checked" (relation-object) variants, makes Create/Update inputs an XOR between those variants, updates the TS schema generator to emit model-scoped aliases, and adds compile-time and runtime tests validating the checked/unchecked behavior.

Changes

Cohort / File(s) Summary
Core ORM Types
packages/orm/src/client/crud-types.ts
Introduces exported UncheckedCreateInput/CheckedCreateInput and UncheckedUpdateInput/CheckedUpdateInput; changes CreateInput/UpdateInput to an XOR between checked vs unchecked variants; adjusts scalar update types to exclude FK fields and adds UpdateFKPayload / UpdateNonOwnedRelationInput.
Code Generation
packages/sdk/src/ts-schema-generator.ts
Adds generation of model-scoped aliases for the four new input variants (${Model}UncheckedCreateInput, ${Model}CheckedCreateInput, ${Model}UncheckedUpdateInput, ${Model}CheckedUpdateInput); minor formatting tweaks only.
E2E Typing & Runtime Tests
tests/e2e/orm/client-api/checked-unchecked.test-d.ts, tests/e2e/orm/client-api/checked-unchecked.test.ts
Adds TypeScript-only typing tests asserting distinct checked vs unchecked shapes and XOR constraints; adds runtime tests asserting validation rejects mixed FK+relation payloads and accepts exclusive FK-only or relation-only payloads.
Update Test Annotation
tests/e2e/orm/client-api/update.test.ts
Replaces a generic TODO with a specific @ts-expect-error comment clarifying the XOR typing constraint for FK vs relation mixing.
Test Input Aliases
tests/e2e/orm/schemas/basic/input.ts
Imports new generic input types and exports model-scoped type aliases for unchecked/checked create/update inputs for all models (User, Post, Comment, Profile, Plain).
Regression Fixture: issue-2567
tests/regression/test/issue-2567/schema.zmodel, tests/regression/test/issue-2567/schema.ts, tests/regression/test/issue-2567/models.ts, tests/regression/test/issue-2567/input.ts, tests/regression/test/issue-2567/regression.test.ts
Adds a full regression test fixture and tests verifying FK-only create/update flows (exports generated models/input aliases and runtime regression).
UpdateMany E2E Test
tests/e2e/orm/client-api/update-many.test.ts
Adds test ensuring updateMany accepts FK fields in data and correctly reassigns relations across multiple records.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related issues

Poem

🐇 I nibble types in fields of code,
Split into paths where two ways go,
FK hops this side, relations that—
XOR gates guard the garden flat.
🥕✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly and concisely summarizes the main changes: exporting new checked/unchecked input types and adding XOR logic to UpdateInput, which directly address issue #2567.
Linked Issues check ✅ Passed PR fully addresses issue #2567 by exporting four new input types (UncheckedCreateInput, CheckedCreateInput, UncheckedUpdateInput, CheckedUpdateInput) with proper XOR constraints, enabling Partial spreads to work with FK fields without type errors.
Out of Scope Changes check ✅ Passed All changes are within scope: core type refactoring in crud-types.ts, generator updates, regression test for issue #2567, e2e tests for checked/unchecked patterns, and minor documentation updates in existing tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-2567-unchecked-create-update-input

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 29, 2026

Claude finished @ymc9's task —— View job


PR Review Analysis

Reviewing PR #2627: Export UncheckedCreateInput/CheckedCreateInput and add XOR to UpdateInput

Todo List:

  • Read core implementation changes in crud-types.ts
  • Review TypeScript schema generator changes
  • Analyze test coverage and quality
  • Check TypeScript type safety improvements
  • Assess potential breaking changes and edge cases
  • Provide comprehensive review feedback

Review Feedback

This PR addresses a real TypeScript issue with XOR union collapse when using Partial<CreateArgs['data']> in builder patterns. The solution mirrors Prisma 6's approach and is well-implemented. Here's my detailed analysis:

Strengths

1. Sound Technical Solution

  • Root Cause Analysis: The issue with Partial<XOR<A,B>> collapsing exclusive keys from ?: never to ?: T | undefined is correctly identified and solved
  • Prisma Alignment: Exporting separate UncheckedCreateInput/CheckedCreateInput types mirrors Prisma 6's established pattern
  • Consistency: Applies the same pattern to both create and update operations

2. Excellent Type Safety Improvements

  • XOR Enforcement: UpdateInput now properly enforces mutual exclusion between FK fields and relation objects (previously missing)
  • Non-Owned Relations: Thoughtful handling of relations like Post.comments that appear in both branches
  • Clean Separation: Clear distinction between unchecked (FK-based) and checked (relation object-based) inputs

3. Comprehensive Test Coverage

  • Regression Test: tests/regression/test/issue-2567/ directly validates the original bug fix
  • Runtime Tests: checked-unchecked.test.ts verifies XOR enforcement at runtime
  • Type-Level Tests: checked-unchecked.test-d.ts ensures compile-time type safety
  • Integration: Tests cover both create and update operations

📝 Code Quality Observations

1. Implementation Details (packages/orm/src/client/crud-types.ts:1463-1783)

  • Non-Owned Relations Logic: The NonOwnedRelationFields helper correctly identifies relations without references (lines 2496-2502)
  • XOR Pattern: Proper use of the existing XOR utility for mutual exclusion
  • Documentation: Clear JSDoc comments explaining the purpose of each type

2. Schema Generator Updates (packages/sdk/src/ts-schema-generator.ts:1619-1622)

  • Type Exports: Correctly adds the four new types to the export list
  • Naming Convention: Follows established patterns for exported type names

⚠️ Potential Considerations

1. Breaking Change Assessment

  • New XOR on UpdateInput: Previously, users could mix FK and relation fields in updates (though not recommended)
  • Mitigation: The change enforces best practices and aligns with Prisma's behavior
  • Impact: Low - most codebases should already follow the correct patterns

2. Bundle Size

  • Type Complexity: Additional type exports increase TypeScript compilation overhead
  • Assessment: Minimal impact since these are type-only exports

3. Migration Path

  • Existing Code: Code using CreateArgs['data'] continues to work unchanged
  • Builder Patterns: Can now use Partial<PostUncheckedCreateInput> instead of the problematic Partial<CreateArgs['data']>>

🔍 Edge Cases Considered

The implementation correctly handles:

  • Delegate Models: Discriminator fields are excluded from assignment
  • Array Fields: Proper payload handling for array relations
  • Optional Relations: Correct nullability for optional to-one relations
  • Computed Fields: No impact on computed field evaluation

🚀 Performance & Security

  • Performance: No runtime impact - these are compile-time type improvements
  • Security: No security implications - maintains existing access control patterns
  • Memory: Type-only changes with no runtime footprint

📋 Recommendations

  1. Consider Documentation: Update any migration guides or documentation that references the old Partial<CreateArgs['data']> pattern
  2. Monitor Feedback: Track community response to the UpdateInput XOR enforcement change
  3. Type Tests: The type-level tests provide excellent coverage and should be maintained

Conclusion

This is a well-designed and thoroughly tested improvement that:

  • ✅ Solves a real TypeScript ergonomics issue
  • ✅ Improves type safety consistency
  • ✅ Follows established patterns from the Prisma ecosystem
  • ✅ Includes comprehensive test coverage
  • ✅ Has minimal breaking change risk

The implementation is production-ready and represents a meaningful improvement to the developer experience.

Recommendation: APPROVE


Copy link
Copy Markdown
Contributor

@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: 2

🤖 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/orm/src/client/crud-types.ts`:
- Around line 1654-1657: UpdateManyPayload.data currently only allows
UpdateScalarInput<Schema, Model> which excludes ForeignKeyFields; change it to
also accept FK updates by widening the type to match the
CreateManyPayload/UpdateInput pattern — e.g. replace the single
UpdateScalarInput usage in UpdateManyPayload.data with a union (or the
UncheckedUpdateInput equivalent) that includes the FK payload type
(UpdateFKPayload or CreateFKPayload style) so ForeignKeyFields<Schema, Model>
are accepted during updateMany; update the type alias referenced as
UpdateManyPayload.data and ensure consistency with CreateManyPayload.data and
UpdateInput.

In `@tests/regression/test/issue-2567/regression.test.ts`:
- Around line 1-36: Rename the test file currently named regression.test.ts to
issue-2567.test.ts so it follows the regression-test naming convention; keep the
file in the same tests/regression/test directory and ensure any imports or CI
patterns that reference the old filename are updated if necessary so the suite
remains discoverable as the regression test for issue `#2567`.
🪄 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: 4fa7d3b1-0ff0-4b49-a9f9-0595e3155247

📥 Commits

Reviewing files that changed from the base of the PR and between d30ebad and 96ac1aa.

📒 Files selected for processing (11)
  • packages/orm/src/client/crud-types.ts
  • packages/sdk/src/ts-schema-generator.ts
  • tests/e2e/orm/client-api/checked-unchecked.test-d.ts
  • tests/e2e/orm/client-api/checked-unchecked.test.ts
  • tests/e2e/orm/client-api/update.test.ts
  • tests/e2e/orm/schemas/basic/input.ts
  • tests/regression/test/issue-2567/input.ts
  • tests/regression/test/issue-2567/models.ts
  • tests/regression/test/issue-2567/regression.test.ts
  • tests/regression/test/issue-2567/schema.ts
  • tests/regression/test/issue-2567/schema.zmodel

Comment thread packages/orm/src/client/crud-types.ts
Comment thread tests/regression/test/issue-2567/regression.test.ts
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 29, 2026

Claude finished @ymc9's task —— View job


PR Review Analysis

Reviewing PR #2627: Export UncheckedCreateInput/CheckedCreateInput and add XOR to UpdateInput

Todo List:

  • Read core implementation changes in crud-types.ts
  • Review TypeScript schema generator changes
  • Analyze test coverage and quality
  • Check TypeScript type safety improvements
  • Assess potential breaking changes and edge cases
  • Provide comprehensive review feedback

Review Feedback

This PR addresses a real TypeScript issue with XOR union collapse when using Partial<CreateArgs['data']> in builder patterns. The solution mirrors Prisma 6's approach and is well-implemented. Here's my detailed analysis:

Strengths

1. Sound Technical Solution

  • Root Cause Analysis: The issue with Partial<XOR<A,B>> collapsing exclusive keys from ?: never to ?: T | undefined is correctly identified and solved
  • Prisma Alignment: Exporting separate UncheckedCreateInput/CheckedCreateInput types mirrors Prisma 6's established pattern
  • Consistency: Applies the same pattern to both create and update operations

2. Excellent Type Safety Improvements

  • XOR Enforcement: UpdateInput now properly enforces mutual exclusion between FK fields and relation objects (previously missing)
  • Non-Owned Relations: Thoughtful handling of relations like Post.comments that appear in both branches
  • Clean Separation: Clear distinction between unchecked (FK-based) and checked (relation object-based) inputs

3. Comprehensive Test Coverage

  • Regression Test: tests/regression/test/issue-2567/ directly validates the original bug fix
  • Runtime Tests: checked-unchecked.test.ts verifies XOR enforcement at runtime
  • Type-Level Tests: checked-unchecked.test-d.ts ensures compile-time type safety
  • Integration: Tests cover both create and update operations

📝 Code Quality Observations

1. Implementation Details (packages/orm/src/client/crud-types.ts:1463-1783)

  • Non-Owned Relations Logic: The NonOwnedRelationFields helper correctly identifies relations without references (lines 2496-2502)
  • XOR Pattern: Proper use of the existing XOR utility for mutual exclusion
  • Documentation: Clear JSDoc comments explaining the purpose of each type

2. Schema Generator Updates (packages/sdk/src/ts-schema-generator.ts:1619-1622)

  • Type Exports: Correctly adds the four new types to the export list
  • Naming Convention: Follows established patterns for exported type names

⚠️ Potential Considerations

1. Breaking Change Assessment

  • New XOR on UpdateInput: Previously, users could mix FK and relation fields in updates (though not recommended)
  • Mitigation: The change enforces best practices and aligns with Prisma's behavior
  • Impact: Low - most codebases should already follow the correct patterns

2. Bundle Size

  • Type Complexity: Additional type exports increase TypeScript compilation overhead
  • Assessment: Minimal impact since these are type-only exports

3. Migration Path

  • Existing Code: Code using CreateArgs['data'] continues to work unchanged
  • Builder Patterns: Can now use Partial<PostUncheckedCreateInput> instead of the problematic Partial<CreateArgs['data']>>

🔍 Edge Cases Considered

The implementation correctly handles:

  • Delegate Models: Discriminator fields are excluded from assignment
  • Array Fields: Proper payload handling for array relations
  • Optional Relations: Correct nullability for optional to-one relations
  • Computed Fields: No impact on computed field evaluation

🚀 Performance & Security

  • Performance: No runtime impact - these are compile-time type improvements
  • Security: No security implications - maintains existing access control patterns
  • Memory: Type-only changes with no runtime footprint

📋 Recommendations

  1. Consider Documentation: Update any migration guides or documentation that references the old Partial<CreateArgs['data']> pattern
  2. Monitor Feedback: Track community response to the UpdateInput XOR enforcement change
  3. Type Tests: The type-level tests provide excellent coverage and should be maintained

Conclusion

This is a well-designed and thoroughly tested improvement that:

  • ✅ Solves a real TypeScript ergonomics issue
  • ✅ Improves type safety consistency
  • ✅ Follows established patterns from the Prisma ecosystem
  • ✅ Includes comprehensive test coverage
  • ✅ Has minimal breaking change risk

The implementation is production-ready and represents a meaningful improvement to the developer experience.

Recommendation: APPROVE


@ymc9 ymc9 merged commit 2a10bca into dev Apr 29, 2026
12 checks passed
@ymc9 ymc9 deleted the fix/issue-2567-unchecked-create-update-input branch April 29, 2026 21:44
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.

Type error on create when a relation with a default value is implicitly undefined

1 participant