diff --git a/.changeset/tiny-webs-poke.md b/.changeset/tiny-webs-poke.md new file mode 100644 index 000000000..0e472cba4 --- /dev/null +++ b/.changeset/tiny-webs-poke.md @@ -0,0 +1,7 @@ +--- +'@tanstack/react-form-nextjs': patch +'@tanstack/react-form-remix': patch +'@tanstack/form-core': patch +--- + +Fix type mismatch in `errors` field - exclude undefined to match runtime behavior diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index 1d5ba3096..b93f43578 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -691,37 +691,48 @@ export type FieldMetaDerived< * An array of errors related to the field value. */ errors: Array< - | UnwrapOneLevelOfArray< - UnwrapFieldValidateOrFn - > - | UnwrapOneLevelOfArray< - UnwrapFieldValidateOrFn - > - | UnwrapOneLevelOfArray< - UnwrapFieldAsyncValidateOrFn - > - | UnwrapOneLevelOfArray< - UnwrapFieldValidateOrFn - > - | UnwrapOneLevelOfArray< - UnwrapFieldAsyncValidateOrFn - > - | UnwrapOneLevelOfArray< - UnwrapFieldValidateOrFn - > - | UnwrapOneLevelOfArray< - UnwrapFieldAsyncValidateOrFn - > - | UnwrapOneLevelOfArray< - UnwrapFieldValidateOrFn - > - | UnwrapOneLevelOfArray< - UnwrapFieldAsyncValidateOrFn< - TName, - TOnDynamicAsync, - TFormOnDynamicAsync + Exclude< + | UnwrapOneLevelOfArray< + UnwrapFieldValidateOrFn > - > + | UnwrapOneLevelOfArray< + UnwrapFieldValidateOrFn + > + | UnwrapOneLevelOfArray< + UnwrapFieldAsyncValidateOrFn< + TName, + TOnChangeAsync, + TFormOnChangeAsync + > + > + | UnwrapOneLevelOfArray< + UnwrapFieldValidateOrFn + > + | UnwrapOneLevelOfArray< + UnwrapFieldAsyncValidateOrFn + > + | UnwrapOneLevelOfArray< + UnwrapFieldValidateOrFn + > + | UnwrapOneLevelOfArray< + UnwrapFieldAsyncValidateOrFn< + TName, + TOnSubmitAsync, + TFormOnSubmitAsync + > + > + | UnwrapOneLevelOfArray< + UnwrapFieldValidateOrFn + > + | UnwrapOneLevelOfArray< + UnwrapFieldAsyncValidateOrFn< + TName, + TOnDynamicAsync, + TFormOnDynamicAsync + > + >, + undefined + > > /** * A flag that is `true` if the field's value has not been modified by the user. Opposite of `isDirty`. diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index e7e9fb2ad..9c887b85d 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -684,16 +684,19 @@ export type DerivedFormState< * The error array for the form itself. */ errors: Array< - | UnwrapFormValidateOrFn - | UnwrapFormValidateOrFn - | UnwrapFormAsyncValidateOrFn - | UnwrapFormValidateOrFn - | UnwrapFormAsyncValidateOrFn - | UnwrapFormValidateOrFn - | UnwrapFormAsyncValidateOrFn - | UnwrapFormValidateOrFn - | UnwrapFormAsyncValidateOrFn - | UnwrapFormAsyncValidateOrFn + Exclude< + | UnwrapFormValidateOrFn + | UnwrapFormValidateOrFn + | UnwrapFormAsyncValidateOrFn + | UnwrapFormValidateOrFn + | UnwrapFormAsyncValidateOrFn + | UnwrapFormValidateOrFn + | UnwrapFormAsyncValidateOrFn + | UnwrapFormValidateOrFn + | UnwrapFormAsyncValidateOrFn + | UnwrapFormAsyncValidateOrFn, + undefined + > > /** * A boolean indicating if any of the form fields are currently validating. @@ -1200,14 +1203,19 @@ export class FormApi< ) { errors = Object.values(currBaseStore.errorMap).reduce< Array< - | UnwrapFormValidateOrFn - | UnwrapFormValidateOrFn - | UnwrapFormAsyncValidateOrFn - | UnwrapFormValidateOrFn - | UnwrapFormAsyncValidateOrFn - | UnwrapFormValidateOrFn - | UnwrapFormAsyncValidateOrFn - | UnwrapFormAsyncValidateOrFn + Exclude< + | UnwrapFormValidateOrFn + | UnwrapFormValidateOrFn + | UnwrapFormAsyncValidateOrFn + | UnwrapFormValidateOrFn + | UnwrapFormAsyncValidateOrFn + | UnwrapFormValidateOrFn + | UnwrapFormAsyncValidateOrFn + | UnwrapFormValidateOrFn + | UnwrapFormAsyncValidateOrFn + | UnwrapFormAsyncValidateOrFn, + undefined + > > >((prev, curr) => { if (curr === undefined) return prev diff --git a/packages/form-core/tests/FieldApi.test-d.ts b/packages/form-core/tests/FieldApi.test-d.ts index 838b5c4e1..5fd3ab219 100644 --- a/packages/form-core/tests/FieldApi.test-d.ts +++ b/packages/form-core/tests/FieldApi.test-d.ts @@ -221,9 +221,7 @@ it('should have the correct types returned from field validators in array', () = }, }) - expectTypeOf(field.state.meta.errors).toEqualTypeOf< - Array<'123' | undefined> - >() + expectTypeOf(field.state.meta.errors).toEqualTypeOf>() }) it('should have the correct types returned from form validators in array', () => { @@ -238,7 +236,7 @@ it('should have the correct types returned from form validators in array', () => }, } as const) - expectTypeOf(form.state.errors).toEqualTypeOf>() + expectTypeOf(form.state.errors).toEqualTypeOf>() }) it('should handle "fields" return types added to the field\'s errorMap itself', () => { @@ -288,9 +286,7 @@ it('should handle "fields" return types added to the field\'s error array itself name: 'firstName', }) - expectTypeOf(field.getMeta().errors).toEqualTypeOf< - Array<'Testing' | undefined> - >() + expectTypeOf(field.getMeta().errors).toEqualTypeOf>() }) it('should handle "fields" async return types added to the field\'s errorMap itself', () => { @@ -340,9 +336,7 @@ it('should handle "fields" async return types added to the field\'s error array name: 'firstName', }) - expectTypeOf(field.getMeta().errors).toEqualTypeOf< - Array<'Testing' | undefined> - >() + expectTypeOf(field.getMeta().errors).toEqualTypeOf>() }) it('should handle "sub-fields" async return types added to the field\'s error array itself', () => { @@ -368,9 +362,7 @@ it('should handle "sub-fields" async return types added to the field\'s error ar name: 'person.firstName', }) - expectTypeOf(field.getMeta().errors).toEqualTypeOf< - Array<'Testing' | undefined> - >() + expectTypeOf(field.getMeta().errors).toEqualTypeOf>() }) it('should only have field-level error types returned from parseValueWithSchema and parseValueWithSchemaAsync', () => { diff --git a/packages/form-core/tests/FormApi.test-d.ts b/packages/form-core/tests/FormApi.test-d.ts index 1d2554a6e..40ef5d80c 100644 --- a/packages/form-core/tests/FormApi.test-d.ts +++ b/packages/form-core/tests/FormApi.test-d.ts @@ -365,15 +365,8 @@ it('should extract the form error type from a global form error', () => { >() expectTypeOf(form.state.errors).toEqualTypeOf< - ( - | 'onMount' - | 'onChange' - | 'onChangeAsync' - | 'onBlur' - | 'onBlurAsync' - | undefined - )[] - > + ('onMount' | 'onChange' | 'onChangeAsync' | 'onBlur' | 'onBlurAsync')[] + >() }) it('listeners should be typed correctly', () => { diff --git a/packages/form-core/tests/formOptions.test-d.ts b/packages/form-core/tests/formOptions.test-d.ts index eb7b39a45..fd8089549 100644 --- a/packages/form-core/tests/formOptions.test-d.ts +++ b/packages/form-core/tests/formOptions.test-d.ts @@ -286,9 +286,7 @@ describe('formOptions', () => { const form = new FormApi(formOpts) - expectTypeOf(form.state.errors).toEqualTypeOf< - ('I just need an error' | undefined)[] - >() + expectTypeOf(form.state.errors).toEqualTypeOf<'I just need an error'[]>() const form2 = new FormApi({ ...formOpts, @@ -302,9 +300,7 @@ describe('formOptions', () => { }, }) - expectTypeOf(form2.state.errors).toEqualTypeOf< - (undefined | 'Too short!')[] - >() + expectTypeOf(form2.state.errors).toEqualTypeOf<'Too short!'[]>() const form3 = new FormApi({ ...formOpts, @@ -320,7 +316,7 @@ describe('formOptions', () => { }) expectTypeOf(form3.state.errors).toEqualTypeOf< - (undefined | 'Too short!' | 'I just need an error')[] + ('Too short!' | 'I just need an error')[] >() }) }) diff --git a/packages/react-form-nextjs/src/createServerValidate.ts b/packages/react-form-nextjs/src/createServerValidate.ts index e1af20488..0f2d57e3b 100644 --- a/packages/react-form-nextjs/src/createServerValidate.ts +++ b/packages/react-form-nextjs/src/createServerValidate.ts @@ -118,7 +118,9 @@ export const createServerValidate = onServer: onServerError, }, values, - errors: onServerErrorVal ? [onServerErrorVal] : [], + errors: (onServerErrorVal ? [onServerErrorVal] : []) as Array< + Exclude, undefined> + >, } throw new ServerValidateError({ diff --git a/packages/react-form-remix/src/createServerValidate.ts b/packages/react-form-remix/src/createServerValidate.ts index e1af20488..0f2d57e3b 100644 --- a/packages/react-form-remix/src/createServerValidate.ts +++ b/packages/react-form-remix/src/createServerValidate.ts @@ -118,7 +118,9 @@ export const createServerValidate = onServer: onServerError, }, values, - errors: onServerErrorVal ? [onServerErrorVal] : [], + errors: (onServerErrorVal ? [onServerErrorVal] : []) as Array< + Exclude, undefined> + >, } throw new ServerValidateError({ diff --git a/packages/react-form/tests/useField.test.tsx b/packages/react-form/tests/useField.test.tsx index 861ad9df5..4150ba9c6 100644 --- a/packages/react-form/tests/useField.test.tsx +++ b/packages/react-form/tests/useField.test.tsx @@ -847,7 +847,7 @@ describe('useField', () => { /> {field.state.meta.errors.map((err) => { - return
{err}
+ return
{err}
})} )