From 22b7134dff6952dce7cf6a029240c360da7b945e Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Wed, 16 Apr 2025 10:57:54 +0200 Subject: [PATCH 1/7] feat(FormApi): add standard schema validator wrapper --- packages/form-core/src/FormApi.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 933808307..bfbc0aa8f 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -2066,6 +2066,17 @@ export class FormApi< ), } } + + /** + * FIXME this name is not final. It's merely to show the stateless wrapper this + * method represents. + */ + getValidationIssuesFromSchema(schema: StandardSchemaV1) { + return standardSchemaValidators.validate( + { value: this.state.values, validationSource: 'form' }, + schema, + ) + } } function normalizeError(rawError?: FormValidationError): { From 7036146995358f844d3563e6e6d51389a51aeff1 Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Wed, 16 Apr 2025 12:19:26 +0200 Subject: [PATCH 2/7] feat(form-core): add parseValueWithSchema methods --- packages/form-core/src/FieldApi.ts | 24 +++ packages/form-core/src/FormApi.ts | 19 ++- .../form-core/src/standardSchemaValidator.ts | 61 +++++--- packages/form-core/tests/FieldApi.spec.ts | 93 ++++++++++++ packages/form-core/tests/FormApi.spec.ts | 142 ++++++++++++++++++ 5 files changed, 313 insertions(+), 26 deletions(-) diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index 784debedb..aa408b03e 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1643,6 +1643,30 @@ export class FieldApi< ) } + /** + * Parses the field's value with the given schema and returns + * issues (if any). This method does NOT set any internal errors. + * @param schema The standard schema to parse this field's value with. + */ + parseValueWithSchema(schema: StandardSchemaV1) { + return standardSchemaValidators.validate( + { value: this.state.value, validationSource: 'field' }, + schema, + ) + } + + /** + * Parses the field's value with the given schema and returns + * issues (if any). This method does NOT set any internal errors. + * @param schema The standard schema to parse this field's value with. + */ + parseValueWithSchemaAsync(schema: StandardSchemaV1) { + return standardSchemaValidators.validateAsync( + { value: this.state.value, validationSource: 'field' }, + schema, + ) + } + private triggerOnBlurListener() { const debounceMs = this.options.listeners?.onBlurDebounceMs diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index bfbc0aa8f..a778d9f55 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -2068,15 +2068,28 @@ export class FormApi< } /** - * FIXME this name is not final. It's merely to show the stateless wrapper this - * method represents. + * Parses the form's values with a given standard schema and returns + * issues (if any). This method does NOT set any internal errors. + * @param schema The standard schema to parse the form values with. */ - getValidationIssuesFromSchema(schema: StandardSchemaV1) { + parseValuesWithSchema(schema: StandardSchemaV1) { return standardSchemaValidators.validate( { value: this.state.values, validationSource: 'form' }, schema, ) } + + /** + * Parses the form's values with a given standard schema and returns + * issues (if any). This method does NOT set any internal errors. + * @param schema The standard schema to parse the form values with. + */ + parseValuesWithSchemaAsync(schema: StandardSchemaV1) { + return standardSchemaValidators.validateAsync( + { value: this.state.values, validationSource: 'form' }, + schema, + ) + } } function normalizeError(rawError?: FormValidationError): { diff --git a/packages/form-core/src/standardSchemaValidator.ts b/packages/form-core/src/standardSchemaValidator.ts index 08b6a139e..c0c5bde69 100644 --- a/packages/form-core/src/standardSchemaValidator.ts +++ b/packages/form-core/src/standardSchemaValidator.ts @@ -1,10 +1,24 @@ import type { ValidationSource } from './types' -export type TStandardSchemaValidatorValue = { +export type TStandardSchemaValidatorValue< + TData, + TSource extends ValidationSource = ValidationSource, +> = { value: TData - validationSource: ValidationSource + validationSource: TSource } +export type TStandardSchemaValidatorIssue< + TSource extends ValidationSource = ValidationSource, +> = TSource extends 'form' + ? { + form: Record + fields: Record + } + : TSource extends 'field' + ? StandardSchemaV1Issue[] + : never + function prefixSchemaToErrors(issues: readonly StandardSchemaV1Issue[]) { const schema = new Map() @@ -26,30 +40,24 @@ function prefixSchemaToErrors(issues: readonly StandardSchemaV1Issue[]) { return Object.fromEntries(schema) } -const defaultFieldTransformer = (issues: readonly StandardSchemaV1Issue[]) => - issues - -const defaultFormTransformer = (issues: readonly StandardSchemaV1Issue[]) => { +const transformFormIssues = ( + issues: readonly StandardSchemaV1Issue[], +): TStandardSchemaValidatorIssue => { const schemaErrors = prefixSchemaToErrors(issues) return { form: schemaErrors, fields: schemaErrors, - } + } as TStandardSchemaValidatorIssue } -const transformIssues = ( - validationSource: 'form' | 'field', - issues: readonly StandardSchemaV1Issue[], -) => - validationSource === 'form' - ? defaultFormTransformer(issues) - : defaultFieldTransformer(issues) - export const standardSchemaValidators = { - validate( - { value, validationSource }: TStandardSchemaValidatorValue, + validate( + { + value, + validationSource, + }: TStandardSchemaValidatorValue, schema: StandardSchemaV1, - ) { + ): TStandardSchemaValidatorIssue | undefined { const result = schema['~standard'].validate(value) if (result instanceof Promise) { @@ -58,17 +66,24 @@ export const standardSchemaValidators = { if (!result.issues) return - return transformIssues(validationSource, result.issues) + if (validationSource === 'field') + return result.issues as TStandardSchemaValidatorIssue + return transformFormIssues(result.issues) }, - async validateAsync( - { value, validationSource }: TStandardSchemaValidatorValue, + async validateAsync( + { + value, + validationSource, + }: TStandardSchemaValidatorValue, schema: StandardSchemaV1, - ) { + ): Promise | undefined> { const result = await schema['~standard'].validate(value) if (!result.issues) return - return transformIssues(validationSource, result.issues) + if (validationSource === 'field') + return result.issues as TStandardSchemaValidatorIssue + return transformFormIssues(result.issues) }, } diff --git a/packages/form-core/tests/FieldApi.spec.ts b/packages/form-core/tests/FieldApi.spec.ts index 6d8ed17d4..43834a893 100644 --- a/packages/form-core/tests/FieldApi.spec.ts +++ b/packages/form-core/tests/FieldApi.spec.ts @@ -1,4 +1,5 @@ import { describe, expect, it, vi } from 'vitest' +import { z } from 'zod' import { FieldApi, FormApi } from '../src/index' import { sleep } from './utils' @@ -1954,4 +1955,96 @@ describe('field api', () => { await vi.advanceTimersByTimeAsync(300) expect(onBlurMock).toHaveBeenCalledTimes(1) }) + + it('should pass the current value to the standard schema when calling parseValueWithSchema', async () => { + const form = new FormApi({ + defaultValues: { + firstName: '', + }, + }) + form.mount() + + const field = new FieldApi({ + form, + name: 'firstName', + }) + field.mount() + + // The schema should complain that the value is too short + const firstNameSchemaResult = field.parseValueWithSchema(z.string().min(3)) + expect(firstNameSchemaResult).not.toBeUndefined() + expect(Array.isArray(firstNameSchemaResult)).toBe(true) + expect(firstNameSchemaResult).not.toHaveLength(0) + + field.setValue('some long name that satisfies firstNameSchemaResult') + // the schema should now be satisfied + const successResult = field.parseValueWithSchema(z.string().min(3)) + expect(successResult).toBeUndefined() + }) + + it('should pass the current value to the standard schema when calling parseValueWithSchemaAsync', async () => { + const form = new FormApi({ + defaultValues: { + firstName: '', + }, + }) + form.mount() + + const field = new FieldApi({ + form, + name: 'firstName', + }) + field.mount() + + // The schema should complain that the value is too short + const firstNamePromise = field.parseValueWithSchemaAsync(z.string().min(3)) + expect(firstNamePromise).toBeInstanceOf(Promise) + + const firstNameSchemaResult = await firstNamePromise + + expect(firstNameSchemaResult).not.toBeUndefined() + expect(Array.isArray(firstNameSchemaResult)).toBe(true) + expect(firstNameSchemaResult).not.toHaveLength(0) + + field.setValue('some long name that satisfies firstNameSchemaResult') + // the schema should now be satisfied + const successPromise = field.parseValueWithSchemaAsync(z.string().min(3)) + expect(successPromise).toBeInstanceOf(Promise) + + const successResult = await successPromise + expect(successResult).toBeUndefined() + }) + + it('should throw an error when passing an async schema to parseValueWithSchema', async () => { + const testSchema = z.string().superRefine(async () => { + await sleep(1000) + return true + }) + + const form = new FormApi({ + defaultValues: { + name: '', + }, + }) + form.mount() + + const field = new FieldApi({ + form, + name: 'name', + }) + field.mount() + + // async passed to sync should error + expect(() => { + field.parseValueWithSchema(testSchema) + }).toThrowError() + // async to async is fine + expect(() => { + form.parseValuesWithSchemaAsync(testSchema) + }).not.toThrowError() + // sync to async is also fine + expect(() => { + form.parseValuesWithSchemaAsync(z.any()) + }).not.toThrowError() + }) }) diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index 204fa4dba..56b265779 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -1,4 +1,5 @@ import { describe, expect, it, vi } from 'vitest' +import { z } from 'zod' import { FieldApi, FormApi } from '../src/index' import { sleep } from './utils' @@ -2907,4 +2908,145 @@ describe('form api', () => { expect(form.state.isValid).toBe(false) expect(form.state.canSubmit).toBe(true) }) + + it('should pass the current values to the standard schema when calling parseValuesWithSchema', async () => { + const nameSchema = z.object({ + name: z.string(), + }) + const firstNameSchema = z.object({ + firstName: z.string().min(3), + }) + + const form = new FormApi({ + defaultValues: { + firstName: '', + }, + }) + form.mount() + + const nameSchemaResult = form.parseValuesWithSchema(nameSchema) + // Name schema should complain that 'name' is missing in our form + expect(nameSchemaResult).not.toBeUndefined() + expect(Array.isArray(nameSchemaResult)).not.toBe(true) + + expect(nameSchemaResult).toHaveProperty('fields') + expect(nameSchemaResult).toHaveProperty('form') + + expect(nameSchemaResult?.fields).toHaveProperty('name') + expect(Array.isArray(nameSchemaResult?.fields['name'])).toBe(true) + expect(nameSchemaResult?.fields['name']).not.toHaveLength(0) + + // First name schema should complain that 'firstName' is too short + const firstNameSchemaResult = form.parseValuesWithSchema(firstNameSchema) + expect(firstNameSchemaResult).not.toBeUndefined() + expect(Array.isArray(firstNameSchemaResult)).not.toBe(true) + + expect(firstNameSchemaResult).toHaveProperty('fields') + expect(firstNameSchemaResult).toHaveProperty('form') + + expect(firstNameSchemaResult?.fields).toHaveProperty('firstName') + expect(Array.isArray(firstNameSchemaResult?.fields['firstName'])).toBe(true) + expect(firstNameSchemaResult?.fields['firstName']).not.toHaveLength(0) + + form.setFieldValue( + 'firstName', + 'some long name that satisfies firstNameSchemaResult', + ) + // firstName should now be satisfied + const successResult = form.parseValuesWithSchema(firstNameSchema) + expect(successResult).toBeUndefined() + }) + + it('should pass the current values to the standard schema when calling parseValuesWithSchemaAsync', async () => { + vi.useFakeTimers() + + const nameSchema = z.object({ + name: z.string(), + }) + const firstNameSchema = z.object({ + firstName: z.string().min(3), + }) + + const form = new FormApi({ + defaultValues: { + firstName: '', + }, + }) + form.mount() + + const nameSchemaPromise = form.parseValuesWithSchemaAsync(nameSchema) + expect(nameSchemaPromise).toBeInstanceOf(Promise) + + const nameSchemaResult = await nameSchemaPromise + + // Name schema should complain that 'name' is missing in our form + expect(nameSchemaResult).not.toBeUndefined() + expect(Array.isArray(nameSchemaResult)).not.toBe(true) + + expect(nameSchemaResult).toHaveProperty('fields') + expect(nameSchemaResult).toHaveProperty('form') + + expect(nameSchemaResult?.fields).toHaveProperty('name') + expect(Array.isArray(nameSchemaResult?.fields['name'])).toBe(true) + expect(nameSchemaResult?.fields['name']).not.toHaveLength(0) + + // First name schema should complain that 'firstName' is too short + const firstNamePromise = form.parseValuesWithSchemaAsync(firstNameSchema) + expect(firstNamePromise).toBeInstanceOf(Promise) + + const firstNameSchemaResult = await firstNamePromise + + expect(firstNameSchemaResult).not.toBeUndefined() + expect(Array.isArray(firstNameSchemaResult)).not.toBe(true) + + expect(firstNameSchemaResult).toHaveProperty('fields') + expect(firstNameSchemaResult).toHaveProperty('form') + + expect(firstNameSchemaResult?.fields).toHaveProperty('firstName') + expect(Array.isArray(firstNameSchemaResult?.fields['firstName'])).toBe(true) + expect(firstNameSchemaResult?.fields['firstName']).not.toHaveLength(0) + + form.setFieldValue( + 'firstName', + 'some long name that satisfies firstNameSchemaResult', + ) + + // firstName should now be satisfied + const successPromise = form.parseValuesWithSchemaAsync(firstNameSchema) + expect(successPromise).toBeInstanceOf(Promise) + + const successResult = await successPromise + expect(successResult).toBeUndefined() + }) + + it('should throw an error when passing an async schema to parseValuesWithSchema', async () => { + const testSchema = z + .object({ + name: z.string(), + }) + .superRefine(async () => { + await sleep(1000) + return true + }) + + const form = new FormApi({ + defaultValues: { + name: '', + }, + }) + form.mount() + + // async passed to sync should error + expect(() => { + form.parseValuesWithSchema(testSchema) + }).toThrowError() + // async to async is fine + expect(() => { + form.parseValuesWithSchemaAsync(testSchema) + }).not.toThrowError() + // sync to async is also fine + expect(() => { + form.parseValuesWithSchemaAsync(z.any()) + }).not.toThrowError() + }) }) From a4a3680459c96ece1a14bb0bd026c229fe477976 Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Wed, 16 Apr 2025 14:18:02 +0200 Subject: [PATCH 3/7] docs: add parseValueWithSchema documentation --- docs/framework/angular/guides/validation.md | 35 +++++++++++++++++++++ docs/framework/react/guides/validation.md | 22 +++++++++++++ docs/framework/solid/guides/validation.md | 22 +++++++++++++ docs/framework/vue/guides/validation.md | 27 ++++++++++++++++ 4 files changed, 106 insertions(+) diff --git a/docs/framework/angular/guides/validation.md b/docs/framework/angular/guides/validation.md index 75c255ac6..608817624 100644 --- a/docs/framework/angular/guides/validation.md +++ b/docs/framework/angular/guides/validation.md @@ -499,6 +499,41 @@ export class AppComponent { } ``` +If you need even more control over your Standard Schema validation, you can combine a Standard Schema with a callback function like so: + +```angular-ts +@Component({ + selector: 'app-root', + standalone: true, + imports: [TanStackField], + template: ` + + + + `, +}) +export class AppComponent { + ageValidator: FieldValidateAsyncFn = async ({ + value, + fieldApi, + }) => { + const errors = fieldApi.parseValueWithSchema( + z.number().gte(13, 'You must be 13 to make an account'), + ) + if (errors) return errors + + // continue with your validation + } + + // ... +} +``` + ## Preventing invalid forms from being submitted The `onChange`, `onBlur` etc... callbacks are also run when the form is submitted and the submission is blocked if the form is invalid. diff --git a/docs/framework/react/guides/validation.md b/docs/framework/react/guides/validation.md index 343edb9d9..94142501c 100644 --- a/docs/framework/react/guides/validation.md +++ b/docs/framework/react/guides/validation.md @@ -503,6 +503,28 @@ Async validations on form and field level are supported as well: /> ``` +If you need even more control over your Standard Schema validation, you can combine a Standard Schema with a callback function like so: + +```tsx + { + const errors = fieldApi.parseValueWithSchema( + z.number().gte(13, 'You must be 13 to make an account'), + ) + if (errors) return errors + // continue with your validation + }, + }} + children={(field) => { + return <>{/* ... */} + }} +/> +``` + ## Preventing invalid forms from being submitted The `onChange`, `onBlur` etc... callbacks are also run when the form is submitted and the submission is blocked if the form is invalid. diff --git a/docs/framework/solid/guides/validation.md b/docs/framework/solid/guides/validation.md index 5c4c3ca7f..6d076e44c 100644 --- a/docs/framework/solid/guides/validation.md +++ b/docs/framework/solid/guides/validation.md @@ -390,6 +390,28 @@ Async validations on form and field level are supported as well: /> ``` +If you need even more control over your Standard Schema validation, you can combine a Standard Schema with a callback function like so: + +```tsx + { + const errors = fieldApi.parseValueWithSchema( + z.number().gte(13, 'You must be 13 to make an account'), + ) + + if (errors) return errors + + // continue with your validation + }, + }} + children={(field) => { + return <>{/* ... */} + }} +/> +``` + ## Preventing invalid forms from being submitted The `onChange`, `onBlur` etc... callbacks are also run when the form is submitted and the submission is blocked if the form is invalid. diff --git a/docs/framework/vue/guides/validation.md b/docs/framework/vue/guides/validation.md index 810478790..6a9db48fb 100644 --- a/docs/framework/vue/guides/validation.md +++ b/docs/framework/vue/guides/validation.md @@ -442,6 +442,33 @@ Async validations on form and field level are supported as well: ``` +If you need even more control over your Standard Schema validation, you can combine a Standard Schema with a callback function like so: + +```vue + +``` + ## Preventing invalid forms from being submitted The `onChange`, `onBlur` etc... callbacks are also run when the form is submitted and the submission is blocked if the form is invalid. From bd8456c46186240ec6e45d9d4733ccb8c59e194e Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Wed, 16 Apr 2025 14:21:09 +0200 Subject: [PATCH 4/7] refactor(form-core): rename FormApi#parseValuesWithSchema This is to be consistent with other methods that include the word Values. --- packages/form-core/src/FormApi.ts | 4 ++-- packages/form-core/tests/FieldApi.spec.ts | 4 ++-- packages/form-core/tests/FormApi.spec.ts | 20 +++++++++++--------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index a778d9f55..3baedeeff 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -2072,7 +2072,7 @@ export class FormApi< * issues (if any). This method does NOT set any internal errors. * @param schema The standard schema to parse the form values with. */ - parseValuesWithSchema(schema: StandardSchemaV1) { + parseFieldValuesWithSchema(schema: StandardSchemaV1) { return standardSchemaValidators.validate( { value: this.state.values, validationSource: 'form' }, schema, @@ -2084,7 +2084,7 @@ export class FormApi< * issues (if any). This method does NOT set any internal errors. * @param schema The standard schema to parse the form values with. */ - parseValuesWithSchemaAsync(schema: StandardSchemaV1) { + parseFieldValuesWithSchemaAsync(schema: StandardSchemaV1) { return standardSchemaValidators.validateAsync( { value: this.state.values, validationSource: 'form' }, schema, diff --git a/packages/form-core/tests/FieldApi.spec.ts b/packages/form-core/tests/FieldApi.spec.ts index 43834a893..f00b599f4 100644 --- a/packages/form-core/tests/FieldApi.spec.ts +++ b/packages/form-core/tests/FieldApi.spec.ts @@ -2040,11 +2040,11 @@ describe('field api', () => { }).toThrowError() // async to async is fine expect(() => { - form.parseValuesWithSchemaAsync(testSchema) + field.parseValueWithSchemaAsync(testSchema) }).not.toThrowError() // sync to async is also fine expect(() => { - form.parseValuesWithSchemaAsync(z.any()) + field.parseValueWithSchemaAsync(z.any()) }).not.toThrowError() }) }) diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index 56b265779..ee2cab130 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -2924,7 +2924,7 @@ describe('form api', () => { }) form.mount() - const nameSchemaResult = form.parseValuesWithSchema(nameSchema) + const nameSchemaResult = form.parseFieldValuesWithSchema(nameSchema) // Name schema should complain that 'name' is missing in our form expect(nameSchemaResult).not.toBeUndefined() expect(Array.isArray(nameSchemaResult)).not.toBe(true) @@ -2937,7 +2937,8 @@ describe('form api', () => { expect(nameSchemaResult?.fields['name']).not.toHaveLength(0) // First name schema should complain that 'firstName' is too short - const firstNameSchemaResult = form.parseValuesWithSchema(firstNameSchema) + const firstNameSchemaResult = + form.parseFieldValuesWithSchema(firstNameSchema) expect(firstNameSchemaResult).not.toBeUndefined() expect(Array.isArray(firstNameSchemaResult)).not.toBe(true) @@ -2953,7 +2954,7 @@ describe('form api', () => { 'some long name that satisfies firstNameSchemaResult', ) // firstName should now be satisfied - const successResult = form.parseValuesWithSchema(firstNameSchema) + const successResult = form.parseFieldValuesWithSchema(firstNameSchema) expect(successResult).toBeUndefined() }) @@ -2974,7 +2975,7 @@ describe('form api', () => { }) form.mount() - const nameSchemaPromise = form.parseValuesWithSchemaAsync(nameSchema) + const nameSchemaPromise = form.parseFieldValuesWithSchemaAsync(nameSchema) expect(nameSchemaPromise).toBeInstanceOf(Promise) const nameSchemaResult = await nameSchemaPromise @@ -2991,7 +2992,8 @@ describe('form api', () => { expect(nameSchemaResult?.fields['name']).not.toHaveLength(0) // First name schema should complain that 'firstName' is too short - const firstNamePromise = form.parseValuesWithSchemaAsync(firstNameSchema) + const firstNamePromise = + form.parseFieldValuesWithSchemaAsync(firstNameSchema) expect(firstNamePromise).toBeInstanceOf(Promise) const firstNameSchemaResult = await firstNamePromise @@ -3012,7 +3014,7 @@ describe('form api', () => { ) // firstName should now be satisfied - const successPromise = form.parseValuesWithSchemaAsync(firstNameSchema) + const successPromise = form.parseFieldValuesWithSchemaAsync(firstNameSchema) expect(successPromise).toBeInstanceOf(Promise) const successResult = await successPromise @@ -3038,15 +3040,15 @@ describe('form api', () => { // async passed to sync should error expect(() => { - form.parseValuesWithSchema(testSchema) + form.parseFieldValuesWithSchema(testSchema) }).toThrowError() // async to async is fine expect(() => { - form.parseValuesWithSchemaAsync(testSchema) + form.parseFieldValuesWithSchemaAsync(testSchema) }).not.toThrowError() // sync to async is also fine expect(() => { - form.parseValuesWithSchemaAsync(z.any()) + form.parseFieldValuesWithSchemaAsync(z.any()) }).not.toThrowError() }) }) From 41bda55a4b218d8ddf4ff2ec60ab2de0fbb0d028 Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:31:38 +0200 Subject: [PATCH 5/7] refactor(form-core): adjust unit tests for parseValuesWithSchema --- docs/framework/react/guides/validation.md | 1 - packages/form-core/src/FieldApi.ts | 4 +- packages/form-core/src/FormApi.ts | 8 +- packages/form-core/tests/FieldApi.spec.ts | 34 ++++---- packages/form-core/tests/FieldApi.test-d.ts | 24 ++++++ packages/form-core/tests/FormApi.spec.ts | 86 ++++++------------- packages/form-core/tests/FormApi.test-d.ts | 30 ++++++- .../tests/standardSchemaValidator.test-d.ts | 37 +++++++- 8 files changed, 141 insertions(+), 83 deletions(-) diff --git a/docs/framework/react/guides/validation.md b/docs/framework/react/guides/validation.md index 94142501c..777f03306 100644 --- a/docs/framework/react/guides/validation.md +++ b/docs/framework/react/guides/validation.md @@ -510,7 +510,6 @@ If you need even more control over your Standard Schema validation, you can comb name="age" asyncDebounceMs={500} validators={{ - onChange: 1500, onChangeAsync: async ({ value, fieldApi }) => { const errors = fieldApi.parseValueWithSchema( z.number().gte(13, 'You must be 13 to make an account'), diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index aa408b03e..a95fd5515 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1648,7 +1648,7 @@ export class FieldApi< * issues (if any). This method does NOT set any internal errors. * @param schema The standard schema to parse this field's value with. */ - parseValueWithSchema(schema: StandardSchemaV1) { + parseValueWithSchema = (schema: StandardSchemaV1) => { return standardSchemaValidators.validate( { value: this.state.value, validationSource: 'field' }, schema, @@ -1660,7 +1660,7 @@ export class FieldApi< * issues (if any). This method does NOT set any internal errors. * @param schema The standard schema to parse this field's value with. */ - parseValueWithSchemaAsync(schema: StandardSchemaV1) { + parseValueWithSchemaAsync = (schema: StandardSchemaV1) => { return standardSchemaValidators.validateAsync( { value: this.state.value, validationSource: 'field' }, schema, diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 3baedeeff..81ec15079 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -2072,7 +2072,9 @@ export class FormApi< * issues (if any). This method does NOT set any internal errors. * @param schema The standard schema to parse the form values with. */ - parseFieldValuesWithSchema(schema: StandardSchemaV1) { + parseFieldValuesWithSchema = ( + schema: StandardSchemaV1, + ) => { return standardSchemaValidators.validate( { value: this.state.values, validationSource: 'form' }, schema, @@ -2084,7 +2086,9 @@ export class FormApi< * issues (if any). This method does NOT set any internal errors. * @param schema The standard schema to parse the form values with. */ - parseFieldValuesWithSchemaAsync(schema: StandardSchemaV1) { + parseFieldValuesWithSchemaAsync = ( + schema: StandardSchemaV1, + ) => { return standardSchemaValidators.validateAsync( { value: this.state.values, validationSource: 'form' }, schema, diff --git a/packages/form-core/tests/FieldApi.spec.ts b/packages/form-core/tests/FieldApi.spec.ts index f00b599f4..4ed38a623 100644 --- a/packages/form-core/tests/FieldApi.spec.ts +++ b/packages/form-core/tests/FieldApi.spec.ts @@ -1956,7 +1956,9 @@ describe('field api', () => { expect(onBlurMock).toHaveBeenCalledTimes(1) }) - it('should pass the current value to the standard schema when calling parseValueWithSchema', async () => { + it('should pass the current value to the Standard Schema when calling parseValueWithSchema', async () => { + const schema = z.string().min(3) + const form = new FormApi({ defaultValues: { firstName: '', @@ -1971,18 +1973,20 @@ describe('field api', () => { field.mount() // The schema should complain that the value is too short - const firstNameSchemaResult = field.parseValueWithSchema(z.string().min(3)) - expect(firstNameSchemaResult).not.toBeUndefined() - expect(Array.isArray(firstNameSchemaResult)).toBe(true) - expect(firstNameSchemaResult).not.toHaveLength(0) + const issueResult = field.parseValueWithSchema(schema) + expect(issueResult).toBeDefined() + expect(Array.isArray(issueResult)).toBe(true) + expect(issueResult?.length).toBeGreaterThan(0) field.setValue('some long name that satisfies firstNameSchemaResult') // the schema should now be satisfied - const successResult = field.parseValueWithSchema(z.string().min(3)) + const successResult = field.parseValueWithSchema(schema) expect(successResult).toBeUndefined() }) - it('should pass the current value to the standard schema when calling parseValueWithSchemaAsync', async () => { + it('should pass the current value to the Standard Schema when calling parseValueWithSchemaAsync', async () => { + const schema = z.string().min(3) + const form = new FormApi({ defaultValues: { firstName: '', @@ -1997,25 +2001,25 @@ describe('field api', () => { field.mount() // The schema should complain that the value is too short - const firstNamePromise = field.parseValueWithSchemaAsync(z.string().min(3)) - expect(firstNamePromise).toBeInstanceOf(Promise) + const issuePromise = field.parseValueWithSchemaAsync(schema) + expect(issuePromise).toBeInstanceOf(Promise) - const firstNameSchemaResult = await firstNamePromise + const issueResult = await issuePromise - expect(firstNameSchemaResult).not.toBeUndefined() - expect(Array.isArray(firstNameSchemaResult)).toBe(true) - expect(firstNameSchemaResult).not.toHaveLength(0) + expect(issueResult).toBeDefined() + expect(Array.isArray(issueResult)).toBe(true) + expect(issueResult?.length).toBeGreaterThan(0) field.setValue('some long name that satisfies firstNameSchemaResult') // the schema should now be satisfied - const successPromise = field.parseValueWithSchemaAsync(z.string().min(3)) + const successPromise = field.parseValueWithSchemaAsync(schema) expect(successPromise).toBeInstanceOf(Promise) const successResult = await successPromise expect(successResult).toBeUndefined() }) - it('should throw an error when passing an async schema to parseValueWithSchema', async () => { + it('should throw an error when passing an async Standard Schema to parseValueWithSchema', async () => { const testSchema = z.string().superRefine(async () => { await sleep(1000) return true diff --git a/packages/form-core/tests/FieldApi.test-d.ts b/packages/form-core/tests/FieldApi.test-d.ts index 92ef5c99b..5187fe97b 100644 --- a/packages/form-core/tests/FieldApi.test-d.ts +++ b/packages/form-core/tests/FieldApi.test-d.ts @@ -1,5 +1,7 @@ import { assertType, describe, it } from 'vitest' +import { z } from 'zod' import { FieldApi, FormApi } from '../src/index' +import type { StandardSchemaV1Issue } from '../src/index' it('should type value properly', () => { const form = new FormApi({ @@ -356,3 +358,25 @@ it('should handle "sub-fields" async return types added to the field\'s error ar assertType>(field.getMeta().errors) }) + +it('should only have field-level error types returned from parseValueWithSchema and parseValueWithSchemaAsync', () => { + const form = new FormApi({ + defaultValues: { name: '' }, + }) + form.mount() + + const field = new FieldApi({ + form, + name: 'name', + }) + field.mount() + + const schema = z.string() + // assert that it doesn't think it's a form-level error + assertType( + field.parseValueWithSchema(schema), + ) + assertType>( + field.parseValueWithSchemaAsync(schema), + ) +}) diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index ee2cab130..2f0a911e0 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -2909,11 +2909,8 @@ describe('form api', () => { expect(form.state.canSubmit).toBe(true) }) - it('should pass the current values to the standard schema when calling parseValuesWithSchema', async () => { - const nameSchema = z.object({ - name: z.string(), - }) - const firstNameSchema = z.object({ + it('should pass the current values to the Standard Schema when calling parseValuesWithSchema', async () => { + const schema = z.object({ firstName: z.string().min(3), }) @@ -2924,47 +2921,31 @@ describe('form api', () => { }) form.mount() - const nameSchemaResult = form.parseFieldValuesWithSchema(nameSchema) - // Name schema should complain that 'name' is missing in our form - expect(nameSchemaResult).not.toBeUndefined() - expect(Array.isArray(nameSchemaResult)).not.toBe(true) - - expect(nameSchemaResult).toHaveProperty('fields') - expect(nameSchemaResult).toHaveProperty('form') - - expect(nameSchemaResult?.fields).toHaveProperty('name') - expect(Array.isArray(nameSchemaResult?.fields['name'])).toBe(true) - expect(nameSchemaResult?.fields['name']).not.toHaveLength(0) - // First name schema should complain that 'firstName' is too short - const firstNameSchemaResult = - form.parseFieldValuesWithSchema(firstNameSchema) - expect(firstNameSchemaResult).not.toBeUndefined() - expect(Array.isArray(firstNameSchemaResult)).not.toBe(true) + const issueResult = form.parseFieldValuesWithSchema(schema) + expect(issueResult).toBeDefined() + expect(Array.isArray(issueResult)).toBe(false) - expect(firstNameSchemaResult).toHaveProperty('fields') - expect(firstNameSchemaResult).toHaveProperty('form') + expect(issueResult).toHaveProperty('fields') + expect(issueResult).toHaveProperty('form') - expect(firstNameSchemaResult?.fields).toHaveProperty('firstName') - expect(Array.isArray(firstNameSchemaResult?.fields['firstName'])).toBe(true) - expect(firstNameSchemaResult?.fields['firstName']).not.toHaveLength(0) + expect(issueResult?.fields).toHaveProperty('firstName') + expect(Array.isArray(issueResult?.fields['firstName'])).toBe(true) + expect(issueResult?.fields['firstName']?.length).toBeGreaterThan(0) form.setFieldValue( 'firstName', 'some long name that satisfies firstNameSchemaResult', ) // firstName should now be satisfied - const successResult = form.parseFieldValuesWithSchema(firstNameSchema) + const successResult = form.parseFieldValuesWithSchema(schema) expect(successResult).toBeUndefined() }) - it('should pass the current values to the standard schema when calling parseValuesWithSchemaAsync', async () => { + it('should pass the current values to the Standard Schema when calling parseValuesWithSchemaAsync', async () => { vi.useFakeTimers() - const nameSchema = z.object({ - name: z.string(), - }) - const firstNameSchema = z.object({ + const schema = z.object({ firstName: z.string().min(3), }) @@ -2975,38 +2956,21 @@ describe('form api', () => { }) form.mount() - const nameSchemaPromise = form.parseFieldValuesWithSchemaAsync(nameSchema) - expect(nameSchemaPromise).toBeInstanceOf(Promise) - - const nameSchemaResult = await nameSchemaPromise - - // Name schema should complain that 'name' is missing in our form - expect(nameSchemaResult).not.toBeUndefined() - expect(Array.isArray(nameSchemaResult)).not.toBe(true) - - expect(nameSchemaResult).toHaveProperty('fields') - expect(nameSchemaResult).toHaveProperty('form') - - expect(nameSchemaResult?.fields).toHaveProperty('name') - expect(Array.isArray(nameSchemaResult?.fields['name'])).toBe(true) - expect(nameSchemaResult?.fields['name']).not.toHaveLength(0) - // First name schema should complain that 'firstName' is too short - const firstNamePromise = - form.parseFieldValuesWithSchemaAsync(firstNameSchema) - expect(firstNamePromise).toBeInstanceOf(Promise) + const issuePromise = form.parseFieldValuesWithSchemaAsync(schema) + expect(issuePromise).toBeInstanceOf(Promise) - const firstNameSchemaResult = await firstNamePromise + const issueResult = await issuePromise - expect(firstNameSchemaResult).not.toBeUndefined() - expect(Array.isArray(firstNameSchemaResult)).not.toBe(true) + expect(issueResult).toBeDefined() + expect(Array.isArray(issueResult)).toBe(false) - expect(firstNameSchemaResult).toHaveProperty('fields') - expect(firstNameSchemaResult).toHaveProperty('form') + expect(issueResult).toHaveProperty('fields') + expect(issueResult).toHaveProperty('form') - expect(firstNameSchemaResult?.fields).toHaveProperty('firstName') - expect(Array.isArray(firstNameSchemaResult?.fields['firstName'])).toBe(true) - expect(firstNameSchemaResult?.fields['firstName']).not.toHaveLength(0) + expect(issueResult?.fields).toHaveProperty('firstName') + expect(Array.isArray(issueResult?.fields['firstName'])).toBe(true) + expect(issueResult?.fields['firstName']?.length).toBeGreaterThan(0) form.setFieldValue( 'firstName', @@ -3014,14 +2978,14 @@ describe('form api', () => { ) // firstName should now be satisfied - const successPromise = form.parseFieldValuesWithSchemaAsync(firstNameSchema) + const successPromise = form.parseFieldValuesWithSchemaAsync(schema) expect(successPromise).toBeInstanceOf(Promise) const successResult = await successPromise expect(successResult).toBeUndefined() }) - it('should throw an error when passing an async schema to parseValuesWithSchema', async () => { + it('should throw an error when passing an async Standard Schema to parseValuesWithSchema', async () => { const testSchema = z .object({ name: z.string(), diff --git a/packages/form-core/tests/FormApi.test-d.ts b/packages/form-core/tests/FormApi.test-d.ts index e7ab5362c..153eb425f 100644 --- a/packages/form-core/tests/FormApi.test-d.ts +++ b/packages/form-core/tests/FormApi.test-d.ts @@ -1,6 +1,11 @@ import { assertType, it } from 'vitest' +import { z } from 'zod' import { FormApi } from '../src' -import type { ValidationError, ValidationErrorMap } from '../src' +import type { + StandardSchemaV1Issue, + ValidationError, + ValidationErrorMap, +} from '../src' it('should return all errors matching the right type from getAllErrors', () => { const form = new FormApi({ @@ -84,3 +89,26 @@ it('should type handleChange correctly', () => { form.handleSubmit, ) }) + +type FormLevelStandardSchemaIssue = { + form: Record + fields: Record +} + +it('should only have form-level error types returned from parseFieldValuesWithSchema and parseFieldValuesWithSchemaAsync', () => { + const form = new FormApi({ + defaultValues: { name: '' }, + }) + form.mount() + + const schema = z.object({ + name: z.string(), + }) + // assert that it doesn't think it's a field-level error + assertType( + form.parseFieldValuesWithSchema(schema), + ) + assertType>( + form.parseFieldValuesWithSchemaAsync(schema), + ) +}) diff --git a/packages/form-core/tests/standardSchemaValidator.test-d.ts b/packages/form-core/tests/standardSchemaValidator.test-d.ts index dee277ddc..ad90d8564 100644 --- a/packages/form-core/tests/standardSchemaValidator.test-d.ts +++ b/packages/form-core/tests/standardSchemaValidator.test-d.ts @@ -1,6 +1,6 @@ import { assertType, describe, it } from 'vitest' import { z } from 'zod' -import { FieldApi, FormApi } from '../src/index' +import { FieldApi, FormApi, standardSchemaValidators } from '../src/index' import type { StandardSchemaV1Issue } from '../src/index' describe('standard schema validator', () => { @@ -56,4 +56,39 @@ describe('standard schema validator', () => { field.getMeta().errorMap.onChange, ) }) + + type FormLevelStandardSchemaIssue = { + form: Record + fields: Record + } + + it('Should return different Standard Schema Issue types from validate based on scope', () => { + const formSourceError = standardSchemaValidators.validate( + { value: '', validationSource: 'form' }, + z.string(), + ) + const fieldSourceError = standardSchemaValidators.validate( + { value: '', validationSource: 'field' }, + z.string(), + ) + + assertType(formSourceError) + assertType(fieldSourceError) + }) + + it('Should return different Standard Schema Issue types from validateAsync based on scope', () => { + const formSourceError = standardSchemaValidators.validateAsync( + { value: '', validationSource: 'form' }, + z.string(), + ) + const fieldSourceError = standardSchemaValidators.validateAsync( + { value: '', validationSource: 'field' }, + z.string(), + ) + + assertType>( + formSourceError, + ) + assertType>(fieldSourceError) + }) }) From 0c88b3cf11badb0afc00f23f44a654152a86f33b Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:56:59 +0200 Subject: [PATCH 6/7] refactor(FormApi): rename parseFieldValuesWithSchema Since we don't pass a field to the method, the term shouldn't be included in the method name. --- packages/form-core/src/FormApi.ts | 6 ++---- packages/form-core/tests/FormApi.spec.ts | 14 +++++++------- packages/form-core/tests/FormApi.test-d.ts | 4 ++-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 81ec15079..e95b295f2 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -2072,9 +2072,7 @@ export class FormApi< * issues (if any). This method does NOT set any internal errors. * @param schema The standard schema to parse the form values with. */ - parseFieldValuesWithSchema = ( - schema: StandardSchemaV1, - ) => { + parseValuesWithSchema = (schema: StandardSchemaV1) => { return standardSchemaValidators.validate( { value: this.state.values, validationSource: 'form' }, schema, @@ -2086,7 +2084,7 @@ export class FormApi< * issues (if any). This method does NOT set any internal errors. * @param schema The standard schema to parse the form values with. */ - parseFieldValuesWithSchemaAsync = ( + parseValuesWithSchemaAsync = ( schema: StandardSchemaV1, ) => { return standardSchemaValidators.validateAsync( diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index 2f0a911e0..79ca9f797 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -2922,7 +2922,7 @@ describe('form api', () => { form.mount() // First name schema should complain that 'firstName' is too short - const issueResult = form.parseFieldValuesWithSchema(schema) + const issueResult = form.parseValuesWithSchema(schema) expect(issueResult).toBeDefined() expect(Array.isArray(issueResult)).toBe(false) @@ -2938,7 +2938,7 @@ describe('form api', () => { 'some long name that satisfies firstNameSchemaResult', ) // firstName should now be satisfied - const successResult = form.parseFieldValuesWithSchema(schema) + const successResult = form.parseValuesWithSchema(schema) expect(successResult).toBeUndefined() }) @@ -2957,7 +2957,7 @@ describe('form api', () => { form.mount() // First name schema should complain that 'firstName' is too short - const issuePromise = form.parseFieldValuesWithSchemaAsync(schema) + const issuePromise = form.parseValuesWithSchemaAsync(schema) expect(issuePromise).toBeInstanceOf(Promise) const issueResult = await issuePromise @@ -2978,7 +2978,7 @@ describe('form api', () => { ) // firstName should now be satisfied - const successPromise = form.parseFieldValuesWithSchemaAsync(schema) + const successPromise = form.parseValuesWithSchemaAsync(schema) expect(successPromise).toBeInstanceOf(Promise) const successResult = await successPromise @@ -3004,15 +3004,15 @@ describe('form api', () => { // async passed to sync should error expect(() => { - form.parseFieldValuesWithSchema(testSchema) + form.parseValuesWithSchema(testSchema) }).toThrowError() // async to async is fine expect(() => { - form.parseFieldValuesWithSchemaAsync(testSchema) + form.parseValuesWithSchemaAsync(testSchema) }).not.toThrowError() // sync to async is also fine expect(() => { - form.parseFieldValuesWithSchemaAsync(z.any()) + form.parseValuesWithSchemaAsync(z.any()) }).not.toThrowError() }) }) diff --git a/packages/form-core/tests/FormApi.test-d.ts b/packages/form-core/tests/FormApi.test-d.ts index 153eb425f..16a7cebad 100644 --- a/packages/form-core/tests/FormApi.test-d.ts +++ b/packages/form-core/tests/FormApi.test-d.ts @@ -106,9 +106,9 @@ it('should only have form-level error types returned from parseFieldValuesWithSc }) // assert that it doesn't think it's a field-level error assertType( - form.parseFieldValuesWithSchema(schema), + form.parseValuesWithSchema(schema), ) assertType>( - form.parseFieldValuesWithSchemaAsync(schema), + form.parseValuesWithSchemaAsync(schema), ) }) From a05f497e9d0ee563ea0bcbf5be5fd5d433ca1475 Mon Sep 17 00:00:00 2001 From: Leonardo Montini Date: Wed, 16 Apr 2025 16:09:47 +0200 Subject: [PATCH 7/7] docs: generate --- docs/reference/classes/fieldapi.md | 57 +++++++++++++++ docs/reference/classes/formapi.md | 69 +++++++++++++++++++ .../functions/isstandardschemavalidator.md | 2 +- docs/reference/index.md | 1 + .../interfaces/standardschemav1issue.md | 6 +- .../type-aliases/standardschemav1.md | 2 +- .../tstandardschemavalidatorissue.md | 18 +++++ .../tstandardschemavalidatorvalue.md | 8 ++- .../variables/standardschemavalidators.md | 26 +++---- 9 files changed, 168 insertions(+), 21 deletions(-) create mode 100644 docs/reference/type-aliases/tstandardschemavalidatorissue.md diff --git a/docs/reference/classes/fieldapi.md b/docs/reference/classes/fieldapi.md index 93daf3c36..2db8fed55 100644 --- a/docs/reference/classes/fieldapi.md +++ b/docs/reference/classes/fieldapi.md @@ -343,6 +343,63 @@ Moves the value at the first specified index to the second specified index. *** +### parseValueWithSchema() + +```ts +parseValueWithSchema(schema): + | undefined + | StandardSchemaV1Issue[] +``` + +Defined in: [packages/form-core/src/FieldApi.ts:1651](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1651) + +Parses the field's value with the given schema and returns +issues (if any). This method does NOT set any internal errors. + +#### Parameters + +##### schema + +[`StandardSchemaV1`](../type-aliases/standardschemav1.md)\<`TData`, `unknown`\> + +The standard schema to parse this field's value with. + +#### Returns + + \| `undefined` + \| [`StandardSchemaV1Issue`](../interfaces/standardschemav1issue.md)[] + +*** + +### parseValueWithSchemaAsync() + +```ts +parseValueWithSchemaAsync(schema): Promise< + | undefined +| StandardSchemaV1Issue[]> +``` + +Defined in: [packages/form-core/src/FieldApi.ts:1663](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1663) + +Parses the field's value with the given schema and returns +issues (if any). This method does NOT set any internal errors. + +#### Parameters + +##### schema + +[`StandardSchemaV1`](../type-aliases/standardschemav1.md)\<`TData`, `unknown`\> + +The standard schema to parse this field's value with. + +#### Returns + +`Promise`\< + \| `undefined` + \| [`StandardSchemaV1Issue`](../interfaces/standardschemav1issue.md)[]\> + +*** + ### pushValue() ```ts diff --git a/docs/reference/classes/formapi.md b/docs/reference/classes/formapi.md index ca2ccfcea..63e9fce4f 100644 --- a/docs/reference/classes/formapi.md +++ b/docs/reference/classes/formapi.md @@ -422,6 +422,75 @@ Moves the value at the first specified index to the second specified index withi *** +### parseValuesWithSchema() + +```ts +parseValuesWithSchema(schema): + | undefined + | { + fields: Record; + form: Record; +} +``` + +Defined in: [packages/form-core/src/FormApi.ts:2075](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L2075) + +Parses the form's values with a given standard schema and returns +issues (if any). This method does NOT set any internal errors. + +#### Parameters + +##### schema + +[`StandardSchemaV1`](../type-aliases/standardschemav1.md)\<`TFormData`, `unknown`\> + +The standard schema to parse the form values with. + +#### Returns + + \| `undefined` + \| \{ + `fields`: `Record`\<`string`, [`StandardSchemaV1Issue`](../interfaces/standardschemav1issue.md)[]\>; + `form`: `Record`\<`string`, [`StandardSchemaV1Issue`](../interfaces/standardschemav1issue.md)[]\>; + \} + +*** + +### parseValuesWithSchemaAsync() + +```ts +parseValuesWithSchemaAsync(schema): Promise< + | undefined + | { + fields: Record; + form: Record; +}> +``` + +Defined in: [packages/form-core/src/FormApi.ts:2087](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L2087) + +Parses the form's values with a given standard schema and returns +issues (if any). This method does NOT set any internal errors. + +#### Parameters + +##### schema + +[`StandardSchemaV1`](../type-aliases/standardschemav1.md)\<`TFormData`, `unknown`\> + +The standard schema to parse the form values with. + +#### Returns + +`Promise`\< + \| `undefined` + \| \{ + `fields`: `Record`\<`string`, [`StandardSchemaV1Issue`](../interfaces/standardschemav1issue.md)[]\>; + `form`: `Record`\<`string`, [`StandardSchemaV1Issue`](../interfaces/standardschemav1issue.md)[]\>; + \}\> + +*** + ### pushFieldValue() ```ts diff --git a/docs/reference/functions/isstandardschemavalidator.md b/docs/reference/functions/isstandardschemavalidator.md index 6f3b6552c..8f9dacc46 100644 --- a/docs/reference/functions/isstandardschemavalidator.md +++ b/docs/reference/functions/isstandardschemavalidator.md @@ -11,7 +11,7 @@ title: isStandardSchemaValidator function isStandardSchemaValidator(validator): validator is StandardSchemaV1 ``` -Defined in: [packages/form-core/src/standardSchemaValidator.ts:75](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L75) +Defined in: [packages/form-core/src/standardSchemaValidator.ts:90](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L90) ## Parameters diff --git a/docs/reference/index.md b/docs/reference/index.md index 74288fa8c..1e6747bf2 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -59,6 +59,7 @@ title: "@tanstack/form-core" - [Nullable](type-aliases/nullable.md) - [ObjectAccessor](type-aliases/objectaccessor.md) - [StandardSchemaV1](type-aliases/standardschemav1.md) +- [TStandardSchemaValidatorIssue](type-aliases/tstandardschemavalidatorissue.md) - [TStandardSchemaValidatorValue](type-aliases/tstandardschemavalidatorvalue.md) - [TupleAccessor](type-aliases/tupleaccessor.md) - [UnknownAccessor](type-aliases/unknownaccessor.md) diff --git a/docs/reference/interfaces/standardschemav1issue.md b/docs/reference/interfaces/standardschemav1issue.md index 119ef8cb3..b2d0b5fe7 100644 --- a/docs/reference/interfaces/standardschemav1issue.md +++ b/docs/reference/interfaces/standardschemav1issue.md @@ -7,7 +7,7 @@ title: StandardSchemaV1Issue # Interface: StandardSchemaV1Issue -Defined in: [packages/form-core/src/standardSchemaValidator.ts:144](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L144) +Defined in: [packages/form-core/src/standardSchemaValidator.ts:159](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L159) The issue interface of the failure output. @@ -19,7 +19,7 @@ The issue interface of the failure output. readonly message: string; ``` -Defined in: [packages/form-core/src/standardSchemaValidator.ts:148](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L148) +Defined in: [packages/form-core/src/standardSchemaValidator.ts:163](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L163) The error message of the issue. @@ -31,6 +31,6 @@ The error message of the issue. readonly optional path: readonly (PropertyKey | StandardSchemaV1PathSegment)[]; ``` -Defined in: [packages/form-core/src/standardSchemaValidator.ts:152](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L152) +Defined in: [packages/form-core/src/standardSchemaValidator.ts:167](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L167) The path of the issue, if any. diff --git a/docs/reference/type-aliases/standardschemav1.md b/docs/reference/type-aliases/standardschemav1.md index 17cb0125f..225ebd75c 100644 --- a/docs/reference/type-aliases/standardschemav1.md +++ b/docs/reference/type-aliases/standardschemav1.md @@ -11,7 +11,7 @@ title: StandardSchemaV1 type StandardSchemaV1 = object; ``` -Defined in: [packages/form-core/src/standardSchemaValidator.ts:83](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L83) +Defined in: [packages/form-core/src/standardSchemaValidator.ts:98](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L98) The Standard Schema interface. diff --git a/docs/reference/type-aliases/tstandardschemavalidatorissue.md b/docs/reference/type-aliases/tstandardschemavalidatorissue.md new file mode 100644 index 000000000..2da9b70f9 --- /dev/null +++ b/docs/reference/type-aliases/tstandardschemavalidatorissue.md @@ -0,0 +1,18 @@ +--- +id: TStandardSchemaValidatorIssue +title: TStandardSchemaValidatorIssue +--- + + + +# Type Alias: TStandardSchemaValidatorIssue\ + +```ts +type TStandardSchemaValidatorIssue = TSource extends "form" ? object : TSource extends "field" ? StandardSchemaV1Issue[] : never; +``` + +Defined in: [packages/form-core/src/standardSchemaValidator.ts:11](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L11) + +## Type Parameters + +• **TSource** *extends* [`ValidationSource`](validationsource.md) = [`ValidationSource`](validationsource.md) diff --git a/docs/reference/type-aliases/tstandardschemavalidatorvalue.md b/docs/reference/type-aliases/tstandardschemavalidatorvalue.md index 313ee227f..28871726d 100644 --- a/docs/reference/type-aliases/tstandardschemavalidatorvalue.md +++ b/docs/reference/type-aliases/tstandardschemavalidatorvalue.md @@ -5,10 +5,10 @@ title: TStandardSchemaValidatorValue -# Type Alias: TStandardSchemaValidatorValue\ +# Type Alias: TStandardSchemaValidatorValue\ ```ts -type TStandardSchemaValidatorValue = object; +type TStandardSchemaValidatorValue = object; ``` Defined in: [packages/form-core/src/standardSchemaValidator.ts:3](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L3) @@ -17,12 +17,14 @@ Defined in: [packages/form-core/src/standardSchemaValidator.ts:3](https://github • **TData** +• **TSource** *extends* [`ValidationSource`](validationsource.md) = [`ValidationSource`](validationsource.md) + ## Type declaration ### validationSource ```ts -validationSource: ValidationSource; +validationSource: TSource; ``` ### value diff --git a/docs/reference/variables/standardschemavalidators.md b/docs/reference/variables/standardschemavalidators.md index 4ee3c9688..405380eca 100644 --- a/docs/reference/variables/standardschemavalidators.md +++ b/docs/reference/variables/standardschemavalidators.md @@ -11,17 +11,21 @@ title: standardSchemaValidators const standardSchemaValidators: object; ``` -Defined in: [packages/form-core/src/standardSchemaValidator.ts:48](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L48) +Defined in: [packages/form-core/src/standardSchemaValidator.ts:53](https://github.com/TanStack/form/blob/main/packages/form-core/src/standardSchemaValidator.ts#L53) ## Type declaration ### validate() +#### Type Parameters + +• **TSource** *extends* [`ValidationSource`](../type-aliases/validationsource.md) = [`ValidationSource`](../type-aliases/validationsource.md) + #### Parameters ##### \_\_namedParameters -[`TStandardSchemaValidatorValue`](../type-aliases/tstandardschemavalidatorvalue.md)\<`unknown`\> +[`TStandardSchemaValidatorValue`](../type-aliases/tstandardschemavalidatorvalue.md)\<`unknown`, `TSource`\> ##### schema @@ -30,19 +34,19 @@ Defined in: [packages/form-core/src/standardSchemaValidator.ts:48](https://githu #### Returns \| `undefined` - \| readonly [`StandardSchemaV1Issue`](../interfaces/standardschemav1issue.md)[] - \| \{ - `fields`: \{\}; - `form`: \{\}; - \} + \| [`TStandardSchemaValidatorIssue`](../type-aliases/tstandardschemavalidatorissue.md)\<`TSource`\> ### validateAsync() +#### Type Parameters + +• **TSource** *extends* [`ValidationSource`](../type-aliases/validationsource.md) + #### Parameters ##### \_\_namedParameters -[`TStandardSchemaValidatorValue`](../type-aliases/tstandardschemavalidatorvalue.md)\<`unknown`\> +[`TStandardSchemaValidatorValue`](../type-aliases/tstandardschemavalidatorvalue.md)\<`unknown`, `TSource`\> ##### schema @@ -52,8 +56,4 @@ Defined in: [packages/form-core/src/standardSchemaValidator.ts:48](https://githu `Promise`\< \| `undefined` - \| readonly [`StandardSchemaV1Issue`](../interfaces/standardschemav1issue.md)[] - \| \{ - `fields`: \{\}; - `form`: \{\}; - \}\> + \| [`TStandardSchemaValidatorIssue`](../type-aliases/tstandardschemavalidatorissue.md)\<`TSource`\>\>