Skip to content

fix(form-core): prioritize StandardSchemaV1 check over FieldValidateFn for callable schemas (fixes #2221)#2227

Open
tsushanth wants to merge 1 commit into
TanStack:mainfrom
tsushanth:fix/arktype-validator-type-inference
Open

fix(form-core): prioritize StandardSchemaV1 check over FieldValidateFn for callable schemas (fixes #2221)#2227
tsushanth wants to merge 1 commit into
TanStack:mainfrom
tsushanth:fix/arktype-validator-type-inference

Conversation

@tsushanth

@tsushanth tsushanth commented Jun 30, 2026

Copy link
Copy Markdown

Root cause

UnwrapFieldValidateOrFn checked FieldValidateFn before StandardSchemaV1. For any callable schema (arktype, and potentially other future StandardSchema implementations), this caused the wrong branch to be taken.

Arktype's Type uses Callable<fn extends Fn> from @ark/util, which makes Type structurally extend its call signature (data: unknown) => TOut | ArkErrors. TypeScript's contravariant parameter check then evaluates:

[ArkType] extends [FieldValidateFn<any, any, any>]
// FieldValidateFn = (props: { value: any, fieldApi: ... }) => unknown
// Contravariance: need  { value: any, fieldApi: ... } extends unknown  ← true
// Return:         need  TOut | ArkErrors extends unknown               ← true
// → true

So the StandardSchemaV1 branch (line 457) was unreachable and errors resolved to ReturnType<ArkType> (string | ArkErrors for a string schema) instead of StandardSchemaV1Issue[].

Fix

Swap the check order in both the field and form-group branches of UnwrapFieldValidateOrFn:

// Before
| ([TValidateOrFn] extends [FieldValidateFn<any, any, any>]   // ← arktype hits this
    ? ReturnType<TValidateOrFn>
    : [TValidateOrFn] extends [StandardSchemaV1<any, any>]     // ← never reached
      ? StandardSchemaV1Issue[]
      : undefined)

// After
| ([TValidateOrFn] extends [StandardSchemaV1<any, any>]        // ← arktype now hits this
    ? StandardSchemaV1Issue[]
    : [TValidateOrFn] extends [FieldValidateFn<any, any, any>] // ← plain functions still work
      ? ReturnType<TValidateOrFn>
      : undefined)

Plain function validators (non-schema) are unaffected — they don't implement StandardSchemaV1 so they fall through to FieldValidateFn as before.

Test

Added a type-level test in standardSchemaValidator.test-d.ts asserting that an arktype type('string>0') field validator produces StandardSchemaV1Issue[] | undefined for errorMap.onChange, not an arktype output type.

Fixes #2221

Summary by CodeRabbit

  • New Features

    • Improved type inference for schema-based form validation, so validation errors are recognized more accurately in supported setups.
  • Bug Fixes

    • Fixed an issue where certain validation results could infer the wrong error type, leading to less precise editor feedback.
  • Tests

    • Added coverage for schema validation to ensure the updated inference behavior stays consistent.

…apFieldValidateOrFn

Arktype's `Type` implements `Callable<fn>` which makes it structurally
extend `(data: unknown) => T`. TypeScript's contravariant parameter check
means `ArkType extends FieldValidateFn<any,any,any>` evaluates to `true`
(the param check reduces to `{value:any,...} extends unknown`), so the
`StandardSchemaV1` branch was never reached and `errors` entries resolved
to `ReturnType<ArkType>` instead of `StandardSchemaV1Issue[]`.

Fix: check `StandardSchemaV1` first on both the field and form-group
branches of `UnwrapFieldValidateOrFn`. Schemas that happen to be callable
(arktype, and future StandardSchema implementations) now take the correct
path; plain function validators still fall through to the `FieldValidateFn`
branch as before.

Adds a type-level test asserting that an arktype `type('string>0')` field
validator produces `StandardSchemaV1Issue[] | undefined` errors, not the
arktype output type.

Fixes TanStack#2221
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 57596cfa-d375-4384-9c8a-51ceeef2e801

📥 Commits

Reviewing files that changed from the base of the PR and between 6a73479 and e3af80d.

📒 Files selected for processing (2)
  • packages/form-core/src/types.ts
  • packages/form-core/tests/standardSchemaValidator.test-d.ts

📝 Walkthrough

Walkthrough

In UnwrapFieldValidateOrFn, the StandardSchemaV1 branch is moved before the FieldValidateFn/FormGroupValidateFn branches in both union arms, ensuring arktype schemas (which are callable) resolve to StandardSchemaV1Issue[] instead of falling through. A new type-level test confirms errorMap.onChange is inferred as undefined | StandardSchemaV1Issue[] with an arktype validator.

StandardSchemaV1 Type Inference Fix

Layer / File(s) Summary
UnwrapFieldValidateOrFn branch reordering + type test
packages/form-core/src/types.ts, packages/form-core/tests/standardSchemaValidator.test-d.ts
StandardSchemaV1 is now checked first in both union arms of UnwrapFieldValidateOrFn. A new vitest test using arktype's type('string>0') asserts that field.getMeta().errorMap.onChange resolves to undefined | StandardSchemaV1Issue[].

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 A branch out of order caused types to go astray,
The schema came second, but should lead the way!
Now StandardSchema hops first in the queue,
And arktype's errors shine clearly through.
All types resolved — hip hip, hooray! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately describes the main fix: StandardSchemaV1 is checked before FieldValidateFn for callable schemas.
Description check ✅ Passed The description covers the root cause, fix, and test, but it omits the template's checklist and release-impact sections.
Linked Issues check ✅ Passed The change matches #2221 by fixing callable arktype schema inference and adding a type-level regression test.
Out of Scope Changes check ✅ Passed Only the targeted type helper and a related test were changed, with no obvious unrelated edits.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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.

@nx-cloud

nx-cloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit e3af80d

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 3m 27s View ↗
nx run-many --target=build --exclude=examples/** ✅ Succeeded 36s View ↗

☁️ Nx Cloud last updated this comment at 2026-07-01 06:37:09 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jul 1, 2026

Copy link
Copy Markdown
More templates

@tanstack/angular-form

npm i https://pkg.pr.new/@tanstack/angular-form@2227

@tanstack/form-core

npm i https://pkg.pr.new/@tanstack/form-core@2227

@tanstack/form-devtools

npm i https://pkg.pr.new/@tanstack/form-devtools@2227

@tanstack/lit-form

npm i https://pkg.pr.new/@tanstack/lit-form@2227

@tanstack/preact-form

npm i https://pkg.pr.new/@tanstack/preact-form@2227

@tanstack/react-form

npm i https://pkg.pr.new/@tanstack/react-form@2227

@tanstack/react-form-devtools

npm i https://pkg.pr.new/@tanstack/react-form-devtools@2227

@tanstack/react-form-nextjs

npm i https://pkg.pr.new/@tanstack/react-form-nextjs@2227

@tanstack/react-form-remix

npm i https://pkg.pr.new/@tanstack/react-form-remix@2227

@tanstack/react-form-start

npm i https://pkg.pr.new/@tanstack/react-form-start@2227

@tanstack/solid-form

npm i https://pkg.pr.new/@tanstack/solid-form@2227

@tanstack/solid-form-devtools

npm i https://pkg.pr.new/@tanstack/solid-form-devtools@2227

@tanstack/svelte-form

npm i https://pkg.pr.new/@tanstack/svelte-form@2227

@tanstack/vue-form

npm i https://pkg.pr.new/@tanstack/vue-form@2227

commit: e3af80d

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.71%. Comparing base (6892ed0) to head (e3af80d).
⚠️ Report is 235 commits behind head on main.
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2227      +/-   ##
==========================================
- Coverage   90.35%   89.71%   -0.64%     
==========================================
  Files          38       65      +27     
  Lines        1752     3199    +1447     
  Branches      444      803     +359     
==========================================
+ Hits         1583     2870    +1287     
- Misses        149      295     +146     
- Partials       20       34      +14     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

[TypeScript] arktype validators lose error type inference — field.state.meta.errors entries appear as undefined

2 participants