Skip to content

Fix FormApi.submit return type#554

Open
erikras-richard-agent wants to merge 1 commit into
mainfrom
fix-submit-return-types-gh368
Open

Fix FormApi.submit return type#554
erikras-richard-agent wants to merge 1 commit into
mainfrom
fix-submit-return-types-gh368

Conversation

@erikras-richard-agent

@erikras-richard-agent erikras-richard-agent commented May 6, 2026

Copy link
Copy Markdown
Contributor

Closes #368.\n\n## Summary\n- updates FormApi.submit to return SubmissionErrors instead of FormValues\n- aligns createForm's submit implementation annotations/casts with onSubmit's documented return value\n\n## Verification\n- yarn test\n- yarn nps typescript\n- yarn nps build

Summary by CodeRabbit

  • New Features
    • Added subscription methods to monitor field and form state changes in real time
    • Added snapshot methods to retrieve current field and form state
    • Enhanced form submission to support submission errors, improving error handling capabilities

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR updates the form submission API to return SubmissionErrors instead of FormValues, and introduces four new public methods (subscribeFieldState, getFieldSnapshot, subscribeFormState, getFormSnapshot) to align with useSyncExternalStore patterns for field and form state subscriptions.

Changes

Submission Errors and useSyncExternalStore API Expansion

Layer / File(s) Summary
Type Definitions
src/types.ts
FormApi interface updated: submit return type changes from Promise<FormValues | undefined> to Promise<SubmissionErrors>. Four new methods added: subscribeFieldState, getFieldSnapshot, subscribeFormState, getFormSnapshot.
Core Implementation
src/FinalForm.ts
Import SubmissionErrors from types. Rewrite submit method to return Promise<SubmissionErrors>. Update async onSubmit path to handle Promise resolving to SubmissionErrors and call complete with SubmissionErrors payload.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • final-form/final-form#521: Both modify the submit flow and submitErrors handling; this PR changes submit's return type and submission flow while the related PR adjusts when submitErrors are cleared.
  • final-form/final-form#539: Both address async submission flows; this PR changes submit to handle SubmissionErrors while the related PR adds tests for async onSubmit/submit state behavior.
  • final-form/final-form#522: Both modify the submit flow in FinalForm.ts; this PR rewrites submit to return SubmissionErrors and adds new APIs while the related PR reorders beforeSubmit versus sync validation.

Suggested reviewers

  • erikras
  • erikras-dinesh-agent

Poem

🐰 The form now speaks in error tongue,
Subscriptions bloom where states are sung,
No values lost—just errors flow,
Snapshots quick to let you know!
A worthy dance of promise and grace.

🚥 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 title 'Fix FormApi.submit return type' directly and specifically describes the main change—updating the submit method's return type from Promise<FormValues | undefined> to Promise.
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.

✏️ 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-submit-return-types-gh368

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.

@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 `@src/FinalForm.ts`:
- Around line 1357-1362: Sync onSubmit return values are being dropped because
the sync branch calls complete(result as SubmissionErrors) but then falls
through to return Promise.resolve(undefined as SubmissionErrors); update the
sync branch in submit (the block handling onSubmit when it returns a
non-Promise) to forward the result of complete (or directly return
Promise.resolve(result as SubmissionErrors)) so the Promise returned by submit
resolves to the errors object; locate the sync path around the onSubmit handling
where complete is invoked and replace the fall-through with a return that
carries the result through.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: a7f7a9b1-3d17-40e2-9523-17a56016d602

📥 Commits

Reviewing files that changed from the base of the PR and between 89f45a2 and 6a5707a.

📒 Files selected for processing (2)
  • src/FinalForm.ts
  • src/types.ts

Comment thread src/FinalForm.ts
Comment on lines 1357 to +1362
} else {
// onSubmit is sync
complete(result as FormValues);
complete(result as SubmissionErrors);
}
}
return Promise.resolve(undefined as FormValues | undefined);
return Promise.resolve(undefined as SubmissionErrors);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find tests that combine sync onSubmit returning errors with awaited submit().
rg -nP -C5 '\bonSubmit\s*[:=]\s*(\([^)]*\)\s*=>\s*\(?\s*\{|jest\.fn\(\(\)\s*=>\s*\(?\s*\{)' --type=ts -g '**/*test*'
echo '---'
# Show all sites that await/return form.submit() and any nearby expect on the resolved value.
rg -nP -C4 '\b(await|return)\s+form\.submit\s*\(' --type=ts

Repository: final-form/final-form

Length of output: 19189


🏁 Script executed:

sed -n '355,385p' src/FinalForm.submission.test.ts

Repository: final-form/final-form

Length of output: 971


🏁 Script executed:

sed -n '335,390p' src/FinalForm.submission.test.ts

Repository: final-form/final-form

Length of output: 1658


🏁 Script executed:

sed -n '410,450p' src/FinalForm.submission.test.ts

Repository: final-form/final-form

Length of output: 1524


🏁 Script executed:

# Search for tests that check the result of form.submit() and look for any with sync onSubmit
rg -n -B10 -A5 'const result = await form\.submit\(\)' src/FinalForm.submission.test.ts | head -100

Repository: final-form/final-form

Length of output: 618


🏁 Script executed:

sed -n '1340,1365p' src/FinalForm.ts

Repository: final-form/final-form

Length of output: 903


🏁 Script executed:

# Find the complete function definition within the submit function
sed -n '1200,1365p' src/FinalForm.ts | grep -A20 'const complete'

Repository: final-form/final-form

Length of output: 838


🏁 Script executed:

# Get the full complete function definition
sed -n '1210,1240p' src/FinalForm.ts

Repository: final-form/final-form

Length of output: 1086


🏁 Script executed:

# Search for the complete function definition
rg -n 'const complete = \(' src/FinalForm.ts -A25

Repository: final-form/final-form

Length of output: 1096


🏁 Script executed:

# Look for all places where form.submit() is awaited and check onSubmit definitions nearby
rg -n -B30 'const result = await form\.submit\(\)|await form\.submit\(\)' src/FinalForm.submission.test.ts | grep -E '(onSubmit|const result|await form\.submit)' | head -40

Repository: final-form/final-form

Length of output: 1733


🏁 Script executed:

# Look at the test where line 375 has "const result = await form.submit()"
sed -n '340,380p' src/FinalForm.submission.test.ts

Repository: final-form/final-form

Length of output: 1333


🏁 Script executed:

# Check if there's a test specifically for sync onSubmit returning errors with awaited result
rg -n -B5 'onSubmit.*=>.*\{' src/FinalForm.submission.test.ts | grep -A10 'onSubmit: .*=>.*{$' | head -60

Repository: final-form/final-form

Length of output: 727


🏁 Script executed:

# Check the test around line 637 that has sync onSubmit returning errors
sed -n '632,680p' src/FinalForm.submission.test.ts

Repository: final-form/final-form

Length of output: 1415


🏁 Script executed:

# Look at the three paths in the submit function more carefully
sed -n '1330,1365p' src/FinalForm.ts

Repository: final-form/final-form

Length of output: 1325


🏁 Script executed:

# Search for all tests that await form.submit() to find which ones test sync vs async onSubmit
rg -n -B20 'const result = await form\.submit\(\)' src/FinalForm.submission.test.ts | grep -E '(it\(|onSubmit.*=|const result)' | head -20

Repository: final-form/final-form

Length of output: 109


🏁 Script executed:

# Check if there are any tests that might be new/modified that test sync onSubmit with awaited result
rg -n 'sync.*onSubmit|onSubmit.*sync' src/FinalForm.submission.test.ts

Repository: final-form/final-form

Length of output: 829


🏁 Script executed:

# Check if there are any new tests added in this PR that test sync onSubmit with awaited result
git log --oneline -20

Repository: final-form/final-form

Length of output: 105


Sync onSubmit returning errors is silently dropped from the resolved Promise.

The asymmetry between the three submit paths breaks the new return-type contract:

  • Async (Promise) path at L1340-1349: returns value from the .then handler → caller gets the errors.
  • Callback path at L1354-1356: resolvePromise(errors) is invoked from complete → caller gets the errors.
  • Sync path at L1357-1361: complete(result as SubmissionErrors) is called for its side-effects only; its return value (the errors) is discarded, and execution falls through to return Promise.resolve(undefined as SubmissionErrors) at L1362.

For a synchronous onSubmit returning an errors object, await form.submit() will resolve to undefined instead of the expected errors object. This path is not covered by the existing test suite.

🛠️ Suggested fix

Forward the result of complete (or short-circuit the fall-through) so the resolved Promise carries the errors from a sync onSubmit:

         } else {
           // onSubmit is sync
-          complete(result as SubmissionErrors);
+          return Promise.resolve(complete(result as SubmissionErrors));
         }
       }
       return Promise.resolve(undefined as SubmissionErrors);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else {
// onSubmit is sync
complete(result as FormValues);
complete(result as SubmissionErrors);
}
}
return Promise.resolve(undefined as FormValues | undefined);
return Promise.resolve(undefined as SubmissionErrors);
} else {
// onSubmit is sync
return Promise.resolve(complete(result as SubmissionErrors));
}
}
return Promise.resolve(undefined as SubmissionErrors);
🤖 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 `@src/FinalForm.ts` around lines 1357 - 1362, Sync onSubmit return values are
being dropped because the sync branch calls complete(result as SubmissionErrors)
but then falls through to return Promise.resolve(undefined as SubmissionErrors);
update the sync branch in submit (the block handling onSubmit when it returns a
non-Promise) to forward the result of complete (or directly return
Promise.resolve(result as SubmissionErrors)) so the Promise returned by submit
resolves to the errors object; locate the sync path around the onSubmit handling
where complete is invoked and replace the fall-through with a return that
carries the result through.

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.

Possibly wrong submit typescript type

1 participant