Skip to content

feat(conform-react): preserve intent value types - #1289

Open
edmundhung wants to merge 1 commit into
mainfrom
edmundhung/preserve-intent-value-types
Open

feat(conform-react): preserve intent value types#1289
edmundhung wants to merge 1 commit into
mainfrom
edmundhung/preserve-intent-value-types

Conversation

@edmundhung

@edmundhung edmundhung commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • bind reset default values to the dispatcher form shape
  • prevent update, insert, and remove values from widening inferred shapes
  • preserve branded field-name inference when the enclosing form shape is untyped

Testing

  • tsc --project packages/conform-react/tsconfig.build.json
  • vitest --config packages/conform-react/vitest.config.ts --project "conform-react (node)" --run tests/types.test-d.ts tests/intent.test.ts

@changeset-bot

changeset-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ac37616

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@conform-to/react Minor
@conform-to/dom Minor
@conform-to/valibot Minor
@conform-to/yup Minor
@conform-to/zod Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 3, 2026

Copy link
Copy Markdown

Deploying conform with  Cloudflare Pages  Cloudflare Pages

Latest commit: ac37616
Status: ✅  Deploy successful!
Preview URL: https://2ac9e31a.conform.pages.dev
Branch Preview URL: https://edmundhung-preserve-intent-v.conform.pages.dev

View logs

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change refines @conform-to/react intent dispatch typings to support result-returning intents. Reset, update, insert, and remove operations now return their configured result type while preserving inferred form and field shapes. Compile-time tests validate literal acceptance, invalid-value rejection, indexed fields, branded names, and DOM metadata.

Changes

Intent inference

Layer / File(s) Summary
Refine intent dispatch contracts
packages/conform-react/future/types.ts
ExtractDispatchSignature now accepts an optional result type parameter and derives result-aware dispatch signatures. Intent definitions propagate FormShape and Result. IntentDispatch.serialize is now a typed function property. Reset, update, insert, and remove intents return their configured result type. Update options explicitly support whole-form, named-field, and indexed-array variants with NoInfer to constrain inference.
Validate literal preservation
packages/conform-react/tests/types.test-d.ts, packages/conform-react/tests/useControl.browser.test.tsx, .changeset/fair-intents-infer.md
Compile-time tests cover accepted and rejected literals in resets, whole-form updates, indexed updates, inserts, and removals in both programmatic and serialized forms. Branded field names preserve schema-specific value types on untyped dispatchers. DOM-derived metadata exposes broadly typed properties. Browser tests confirm update handlers conditionally pass field names. The changeset documents inferred form and field shape preservation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • edmundhung/conform#1191: Introduced the foundational typed built-in intent system that this change refines for result-returning intents.
  • edmundhung/conform#1245: The main PR supersedes this PR's approach to indexed-update typing by using explicit option variants instead of conditional branching.
  • edmundhung/conform#1283: Extended IntentDispatch.serialize and dispatch signature refinements that this change further develops for typed results.

Suggested reviewers: transparent-citizen

Poem

A rabbit checked each typed intent,
And kept each form shape well-meant.
Reset, update, insert, remove,
Keep their precise shapes as they move.
NoInfer guards the narrow way.
🐰 Infer shapes, but don't sway!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preserving intent value types in conform-react.

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.

@pkg-pr-new

pkg-pr-new Bot commented Aug 3, 2026

Copy link
Copy Markdown
More templates

@conform-to/dom

npm i https://pkg.pr.new/@conform-to/dom@1289

@conform-to/react

npm i https://pkg.pr.new/@conform-to/react@1289

@conform-to/valibot

npm i https://pkg.pr.new/@conform-to/valibot@1289

@conform-to/validitystate

npm i https://pkg.pr.new/@conform-to/validitystate@1289

@conform-to/yup

npm i https://pkg.pr.new/@conform-to/yup@1289

@conform-to/zod

npm i https://pkg.pr.new/@conform-to/zod@1289

commit: ac37616

@edmundhung
edmundhung force-pushed the edmundhung/preserve-intent-value-types branch 2 times, most recently from 3ab3727 to 861bb6a Compare August 4, 2026 09:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/conform-react/future/types.ts`:
- Around line 696-700: Update IntentDispatch to accept a ResultType generic and
pass it to ExtractDispatchSignature while keeping serialize fixed to string;
update IntentDispatcher’s IntentHandler mapping to infer and forward its second
generic argument so programmatic dispatch preserves configured non-void results.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: fa07d0f0-f84f-4fb2-af07-773754048e5e

📥 Commits

Reviewing files that changed from the base of the PR and between 3ab3727 and 861bb6a.

📒 Files selected for processing (3)
  • .changeset/fair-intents-infer.md
  • packages/conform-react/future/types.ts
  • packages/conform-react/tests/types.test-d.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (11)
  • GitHub Check: Validate Preview / Epic Stack
  • GitHub Check: Validate Preview / Examples
  • GitHub Check: Future API Tests (ubuntu-latest, webkit, 22)
  • GitHub Check: E2E Tests (ubuntu-latest, firefox, 22)
  • GitHub Check: Future API Tests (macos-latest, chromium, 22)
  • GitHub Check: E2E Tests (ubuntu-latest, webkit, 22)
  • GitHub Check: Future API Tests (macos-latest, webkit, 22)
  • GitHub Check: E2E Tests (macos-latest, chromium, 22)
  • GitHub Check: E2E Tests (windows-latest, chromium, 22)
  • GitHub Check: Future API Tests (windows-latest, chromium, 22)
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-04-05T20:22:11.135Z
Learnt from: edmundhung
Repo: edmundhung/conform PR: 1180
File: docs/api/react/future/useControl.md:361-363
Timestamp: 2026-04-05T20:22:11.135Z
Learning: When reviewing usages of `useControl.register` in `packages/conform-react`, consider both supported registration forms valid: (1) passing a single form control element (`HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLFieldSetElement`), and (2) passing an iterable collection (`NodeList`/`HTMLCollection`) of checkbox/radio inputs that share the same `name` and compatible `type`. Do not flag the collection-based group registration as an error—both patterns are valid and maintained.

Applied to files:

  • packages/conform-react/tests/types.test-d.ts
  • packages/conform-react/future/types.ts
📚 Learning: 2026-07-18T14:14:07.849Z
Learnt from: edmundhung
Repo: edmundhung/conform PR: 1244
File: packages/conform-react/future/intent.ts:70-75
Timestamp: 2026-07-18T14:14:07.849Z
Learning: For the React “future” custom-state API, the `customState` handler map must be captured on form mount and remain unchanged when the form is reset. When reviewing changes to the future `useForm`/custom-state implementation, ensure reset logic does not recreate or mutate the captured handler map (the handler map should stay referentially stable per the contract described in `docs/api/react/future/useForm.md`).

Applied to files:

  • packages/conform-react/future/types.ts
🪛 markdownlint-cli2 (0.23.1)
.changeset/fair-intents-infer.md

[warning] 5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (4)
packages/conform-react/future/types.ts (2)

713-717: LGTM!

Also applies to: 725-746


748-761: 🎯 Functional Correctness

Verify the literal name: undefined path.

OptionalNameUpdateIntentOptions<undefined> permits index?: number and value: unknown. If inference selects this branch for a literal name: undefined, it bypasses the typed whole-form update variant and permits an index that the whole-form variant rejects.

Add direct and serialized regression cases for an invalid root value without index, and for a valid root value with index: 0. If either call compiles, exclude an exactly undefined Name from this branch while retaining support for name types that also contain string.

packages/conform-react/tests/types.test-d.ts (1)

171-288: LGTM!

.changeset/fair-intents-infer.md (1)

1-5: LGTM!

Comment thread packages/conform-react/future/types.ts Outdated
@edmundhung
edmundhung force-pushed the edmundhung/preserve-intent-value-types branch from 861bb6a to cf1347a Compare August 4, 2026 10:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
packages/conform-react/future/types.ts (1)

696-700: ⚠️ Potential issue | 🟠 Major

Forward the configured IntentHandler result type.

IntentDispatch omits a result type and uses ExtractDispatchSignature with its void default. IntentDispatcher at Line 807 matches the second IntentHandler generic as any and does not forward it. A non-void handler therefore has a void programmatic dispatch signature.

Add ResultType to IntentDispatch, then infer and forward it from IntentHandler. Keep serialize fixed to string. Add a compile-time assertion for a non-void handler result.

Proposed fix
 export type IntentDispatch<
 	Intent extends IntentDefinition,
 	FormShape extends Record<string, any> = Record<string, any>,
-> = ExtractDispatchSignature<NormalizeIntentType<Intent>, FormShape> & {
+	ResultType = void,
+> = ExtractDispatchSignature<
+	NormalizeIntentType<Intent>,
+	FormShape,
+	ResultType
+> & {
 	serialize: ExtractDispatchSignature<
 		NormalizeIntentType<Intent>,
 		FormShape,
 		string
 	>;
 };
 
-		? IntentDispatch<Dispatch, FormShape>
+		? IntentDispatch<Dispatch, FormShape, ResultType>
🤖 Prompt for 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.

In `@packages/conform-react/future/types.ts` around lines 696 - 700, Update
IntentDispatch and the IntentDispatcher flow to carry the configured
IntentHandler result type: add a ResultType generic to IntentDispatch, infer it
from IntentHandler, and pass it to ExtractDispatchSignature while keeping
serialize fixed as string. Add a compile-time assertion covering a non-void
handler result.
🤖 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.

Duplicate comments:
In `@packages/conform-react/future/types.ts`:
- Around line 696-700: Update IntentDispatch and the IntentDispatcher flow to
carry the configured IntentHandler result type: add a ResultType generic to
IntentDispatch, infer it from IntentHandler, and pass it to
ExtractDispatchSignature while keeping serialize fixed as string. Add a
compile-time assertion covering a non-void handler result.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7a175380-b369-49a7-83bd-1bba33289de4

📥 Commits

Reviewing files that changed from the base of the PR and between 861bb6a and cf1347a.

📒 Files selected for processing (4)
  • .changeset/fair-intents-infer.md
  • packages/conform-react/future/types.ts
  • packages/conform-react/tests/types.test-d.ts
  • packages/conform-react/tests/useControl.browser.test.tsx
📜 Review details
⏰ Context from checks skipped due to timeout. (15)
  • GitHub Check: Validate Preview / Examples
  • GitHub Check: Validate Preview / Epic Stack
  • GitHub Check: Future API Tests (macos-latest, webkit, 22)
  • GitHub Check: Future API Tests (macos-latest, chromium, 22)
  • GitHub Check: Future API Tests (windows-latest, chromium, 22)
  • GitHub Check: Future API Tests (ubuntu-latest, chromium, 22)
  • GitHub Check: E2E Tests (macos-latest, chromium, 22)
  • GitHub Check: Future API Tests (ubuntu-latest, webkit, 22)
  • GitHub Check: Future API Tests (ubuntu-latest, firefox, 22)
  • GitHub Check: E2E Tests (macos-latest, webkit, 22)
  • GitHub Check: E2E Tests (ubuntu-latest, webkit, 22)
  • GitHub Check: E2E Tests (windows-latest, chromium, 22)
  • GitHub Check: E2E Tests (ubuntu-latest, firefox, 22)
  • GitHub Check: E2E Tests (ubuntu-latest, chromium, 22)
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-04-05T20:22:11.135Z
Learnt from: edmundhung
Repo: edmundhung/conform PR: 1180
File: docs/api/react/future/useControl.md:361-363
Timestamp: 2026-04-05T20:22:11.135Z
Learning: When reviewing usages of `useControl.register` in `packages/conform-react`, consider both supported registration forms valid: (1) passing a single form control element (`HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLFieldSetElement`), and (2) passing an iterable collection (`NodeList`/`HTMLCollection`) of checkbox/radio inputs that share the same `name` and compatible `type`. Do not flag the collection-based group registration as an error—both patterns are valid and maintained.

Applied to files:

  • packages/conform-react/tests/useControl.browser.test.tsx
  • packages/conform-react/tests/types.test-d.ts
  • packages/conform-react/future/types.ts
📚 Learning: 2026-07-18T14:14:07.849Z
Learnt from: edmundhung
Repo: edmundhung/conform PR: 1244
File: packages/conform-react/future/intent.ts:70-75
Timestamp: 2026-07-18T14:14:07.849Z
Learning: For the React “future” custom-state API, the `customState` handler map must be captured on form mount and remain unchanged when the form is reset. When reviewing changes to the future `useForm`/custom-state implementation, ensure reset logic does not recreate or mutate the captured handler map (the handler map should stay referentially stable per the contract described in `docs/api/react/future/useForm.md`).

Applied to files:

  • packages/conform-react/future/types.ts
🪛 markdownlint-cli2 (0.23.1)
.changeset/fair-intents-infer.md

[warning] 5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (4)
packages/conform-react/future/types.ts (1)

646-676: LGTM!

Also applies to: 713-778

packages/conform-react/tests/types.test-d.ts (1)

171-288: LGTM!

packages/conform-react/tests/useControl.browser.test.tsx (1)

1125-1136: LGTM!

.changeset/fair-intents-infer.md (1)

1-5: LGTM!

@edmundhung
edmundhung force-pushed the edmundhung/preserve-intent-value-types branch from cf1347a to ac37616 Compare August 4, 2026 21:12
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.

1 participant