From 1e47df58ccc3510637e85b5738405521eeb44e5d Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Tue, 5 Dec 2023 18:30:42 -0800 Subject: [PATCH] chore: fix typings --- packages/form-core/src/FieldApi.ts | 166 +++++++++++------- packages/form-core/src/FormApi.ts | 136 ++++++++------ packages/form-core/src/tests/FieldApi.spec.ts | 2 +- packages/form-core/src/tests/FormApi.spec.ts | 4 +- packages/react-form/src/createFormFactory.ts | 24 ++- packages/react-form/src/formContext.ts | 4 +- .../src/tests/createFormFactory.test.tsx | 2 +- .../react-form/src/tests/useField.test.tsx | 24 +-- .../react-form/src/tests/useForm.test.tsx | 16 +- packages/react-form/src/types.ts | 17 +- packages/react-form/src/useField.tsx | 100 +++++++---- packages/react-form/src/useForm.tsx | 21 ++- packages/solid-form/src/createField.tsx | 99 ++++++----- packages/solid-form/src/createForm.tsx | 25 ++- packages/solid-form/src/createFormFactory.ts | 24 ++- packages/solid-form/src/formContext.ts | 4 +- .../solid-form/src/tests/createField.test.tsx | 16 +- .../solid-form/src/tests/createForm.test.tsx | 16 +- .../src/tests/createFormFactory.test.tsx | 2 +- packages/solid-form/src/types.ts | 17 +- .../src/tests/FieldApi.spec.ts | 3 +- .../valibot-form-adapter/src/validator.ts | 11 +- packages/vue-form/src/createFormFactory.ts | 26 +-- packages/vue-form/src/formContext.ts | 14 +- packages/vue-form/src/tests/useField.test.tsx | 10 +- packages/vue-form/src/tests/useForm.test.tsx | 16 +- packages/vue-form/src/types.ts | 17 +- packages/vue-form/src/useField.tsx | 103 +++++++---- packages/vue-form/src/useForm.tsx | 24 ++- .../src/tests/FieldApi.spec.ts | 3 +- packages/yup-form-adapter/src/validator.ts | 6 +- .../src/tests/FieldApi.spec.ts | 3 +- packages/zod-form-adapter/src/validator.ts | 6 +- 33 files changed, 597 insertions(+), 364 deletions(-) diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index 100716b86..8a5e3b7f4 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -12,132 +12,164 @@ import { runValidatorOrAdapter } from './utils' export type FieldValidateFn< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, > = (props: { value: TData - fieldApi: FieldApi + fieldApi: FieldApi }) => ValidationError export type FieldValidateOrFn< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, -> = ValidatorType extends Validator +> = TFieldValidator extends Validator ? | TFN - | FieldValidateFn - : FormValidator extends Validator + | FieldValidateFn< + TParentData, + TName, + TFieldValidator, + TFormValidator, + TData + > + : TFormValidator extends Validator ? | FFN - | FieldValidateFn - : FieldValidateFn + | FieldValidateFn< + TParentData, + TName, + TFieldValidator, + TFormValidator, + TData + > + : FieldValidateFn export type FieldValidateAsyncFn< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, > = (options: { value: TData - fieldApi: FieldApi + fieldApi: FieldApi signal: AbortSignal }) => ValidationError | Promise export type FieldAsyncValidateOrFn< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, -> = ValidatorType extends Validator +> = TFieldValidator extends Validator ? | TFN | FieldValidateAsyncFn< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > - : FormValidator extends Validator + : TFormValidator extends Validator ? | FFN | FieldValidateAsyncFn< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > : FieldValidateAsyncFn< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > export interface FieldValidators< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, > { onMount?: FieldValidateOrFn< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > onChange?: FieldValidateOrFn< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > onChangeAsync?: FieldAsyncValidateOrFn< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > onChangeAsyncDebounceMs?: number onBlur?: FieldValidateOrFn< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > onBlurAsync?: FieldAsyncValidateOrFn< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > onBlurAsyncDebounceMs?: number onSubmit?: FieldValidateOrFn< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > onSubmitAsync?: FieldAsyncValidateOrFn< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > onSubmitAsyncDebounceMs?: number @@ -146,8 +178,12 @@ export interface FieldValidators< export interface FieldOptions< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, > { name: TName @@ -156,12 +192,12 @@ export interface FieldOptions< asyncDebounceMs?: number asyncAlways?: boolean preserveValue?: boolean - validatorAdapter?: ValidatorType + validatorAdapter?: TFieldValidator validators?: FieldValidators< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > defaultMeta?: Partial @@ -170,17 +206,21 @@ export interface FieldOptions< export interface FieldApiOptions< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, > extends FieldOptions< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > { - form: FormApi + form: FormApi } export type FieldMeta = { @@ -205,24 +245,28 @@ export type ResolveName = unknown extends TParentData export class FieldApi< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, > { uid: number form: FieldApiOptions< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData >['form'] name!: DeepKeys options: FieldApiOptions< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData > = {} as any store!: Store> @@ -233,8 +277,8 @@ export class FieldApi< opts: FieldApiOptions< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData >, ) { @@ -322,6 +366,7 @@ export class FieldApi< if (error) { this.setMeta((prev) => ({ ...prev, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition errorMap: { ...prev?.errorMap, onMount: error }, })) } @@ -345,8 +390,8 @@ export class FieldApi< opts: FieldApiOptions< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData >, ) => { @@ -418,7 +463,7 @@ export class FieldApi< TSubData extends DeepValue = DeepValue, >( name: TSubName, - ): FieldApi => + ): FieldApi => new FieldApi({ name: `${this.name}.${name}` as never, form: this.form, @@ -595,6 +640,7 @@ export class FieldApi< return { ...prev, errorMap: { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition ...prev?.errorMap, [getErrorMapKey(cause)]: error, }, diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 09d27c8e3..c6a57d109 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -14,57 +14,73 @@ import type { ValidationErrorMap, Validator, ValidationCause, + ValidationErrorMapKeys, } from './types' -import type { ValidationErrorMapKeys } from './types' -export type FormValidateFn = (props: { - value: TData - formApi: FormApi +export type FormValidateFn< + TFormData, + TFormValidator extends Validator | undefined = undefined, +> = (props: { + value: TFormData + formApi: FormApi }) => ValidationError -export type FormValidateOrFn = - ValidatorType extends Validator - ? TFN - : FormValidateFn - -export type FormValidateAsyncFn = (props: { - value: TData - formApi: FormApi +export type FormValidateOrFn< + TFormData, + TFormValidator extends Validator | undefined = undefined, +> = TFormValidator extends Validator + ? TFN + : FormValidateFn + +export type FormValidateAsyncFn< + TFormData, + TFormValidator extends Validator | undefined = undefined, +> = (props: { + value: TFormData + formApi: FormApi signal: AbortSignal }) => ValidationError | Promise -export type FormAsyncValidateOrFn = - ValidatorType extends Validator - ? FFN | FormValidateAsyncFn - : FormValidateAsyncFn - -export interface FormValidators { - onMount?: FormValidateOrFn - onChange?: FormValidateOrFn - onChangeAsync?: FormAsyncValidateOrFn +export type FormAsyncValidateOrFn< + TFormData, + TFormValidator extends Validator | undefined = undefined, +> = TFormValidator extends Validator + ? FFN | FormValidateAsyncFn + : FormValidateAsyncFn + +export interface FormValidators< + TFormData, + TFormValidator extends Validator | undefined = undefined, +> { + onMount?: FormValidateOrFn + onChange?: FormValidateOrFn + onChangeAsync?: FormAsyncValidateOrFn onChangeAsyncDebounceMs?: number - onBlur?: FormValidateOrFn - onBlurAsync?: FormAsyncValidateOrFn + onBlur?: FormValidateOrFn + onBlurAsync?: FormAsyncValidateOrFn onBlurAsyncDebounceMs?: number - onSubmit?: FormValidateOrFn - onSubmitAsync?: FormAsyncValidateOrFn + onSubmit?: FormValidateOrFn + onSubmitAsync?: FormAsyncValidateOrFn onSubmitAsyncDebounceMs?: number } -export type FormOptions = { - defaultValues?: TData - defaultState?: Partial> +export type FormOptions< + TFormData, + TFormValidator extends Validator | undefined = undefined, +> = { + defaultValues?: TFormData + defaultState?: Partial> asyncAlways?: boolean asyncDebounceMs?: number - validatorAdapter?: ValidatorType - validators?: FormValidators + validatorAdapter?: TFormValidator + validators?: FormValidators onSubmit?: (props: { - value: TData - formApi: FormApi + value: TFormData + formApi: FormApi }) => any | Promise onSubmitInvalid?: (props: { - value: TData - formApi: FormApi + value: TFormData + formApi: FormApi }) => void } @@ -72,13 +88,24 @@ export type ValidationMeta = { lastAbortController: AbortController } -export type FieldInfo = { - instances: Record> +export type FieldInfo< + TFormData, + TFormValidator extends Validator | undefined = undefined, +> = { + instances: Record< + string, + FieldApi< + TFormData, + any, + Validator | undefined, + TFormValidator + > + > validationMetaMap: Record } -export type FormState = { - values: TData +export type FormState = { + values: TFormData // Form Validation isFormValidating: boolean isFormValid: boolean @@ -86,7 +113,7 @@ export type FormState = { errorMap: ValidationErrorMap validationMetaMap: Record // Fields - fieldMeta: Record, FieldMeta> + fieldMeta: Record, FieldMeta> isFieldsValidating: boolean isFieldsValid: boolean isSubmitting: boolean @@ -99,9 +126,9 @@ export type FormState = { submissionAttempts: number } -function getDefaultFormState( - defaultState: Partial>, -): FormState { +function getDefaultFormState( + defaultState: Partial>, +): FormState { return { values: defaultState.values ?? ({} as never), errors: defaultState.errors ?? [], @@ -127,18 +154,21 @@ function getDefaultFormState( } } -export class FormApi { +export class FormApi< + TFormData, + TFormValidator extends Validator | undefined = undefined, +> { // // This carries the context for nested fields - options: FormOptions = {} + options: FormOptions = {} store!: Store> // Do not use __state directly, as it is not reactive. // Please use form.useStore() utility to subscribe to state state!: FormState - fieldInfo: Record, FieldInfo> = + fieldInfo: Record, FieldInfo> = {} as any fieldName?: string - constructor(opts?: FormOptions) { + constructor(opts?: FormOptions) { this.store = new Store>( getDefaultFormState({ ...(opts?.defaultState as any), @@ -217,7 +247,7 @@ export class FormApi { } } - update = (options?: FormOptions) => { + update = (options?: FormOptions) => { if (!options) return this.store.batch(() => { @@ -263,7 +293,7 @@ export class FormApi { const fieldValidationPromises: Promise[] = [] as any this.store.batch(() => { void ( - Object.values(this.fieldInfo) as FieldInfo[] + Object.values(this.fieldInfo) as FieldInfo[] ).forEach((field) => { Object.values(field.instances).forEach((instance) => { // Validate the field @@ -432,9 +462,9 @@ export class FormApi { new Promise(async (resolve) => { let rawError!: ValidationError | undefined try { - rawError = await new Promise((resolve, reject) => { + rawError = await new Promise((rawResolve, rawReject) => { setTimeout(() => { - if (controller.signal.aborted) return resolve(undefined) + if (controller.signal.aborted) return rawResolve(undefined) runValidatorOrAdapter({ validateFn: validateObj.validate, value: { @@ -445,8 +475,8 @@ export class FormApi { methodName: 'validateAsync', adapters: [this.options.validatorAdapter as never], }) - .then(resolve) - .catch(reject) + .then(rawResolve) + .catch(rawReject) }, onChangeAsyncDebounceMs) }) } catch (e: unknown) { @@ -567,7 +597,7 @@ export class FormApi { getFieldInfo = >( field: TField, - ): FieldInfo => { + ): FieldInfo => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition return (this.fieldInfo[field] ||= { instances: {}, diff --git a/packages/form-core/src/tests/FieldApi.spec.ts b/packages/form-core/src/tests/FieldApi.spec.ts index 51d63de82..e203ae31a 100644 --- a/packages/form-core/src/tests/FieldApi.spec.ts +++ b/packages/form-core/src/tests/FieldApi.spec.ts @@ -573,7 +573,7 @@ describe('field api', () => { interface Form { name: string } - const form = new FormApi() + const form = new FormApi
() const field = new FieldApi({ form, diff --git a/packages/form-core/src/tests/FormApi.spec.ts b/packages/form-core/src/tests/FormApi.spec.ts index 6be9a81e9..125faf021 100644 --- a/packages/form-core/src/tests/FormApi.spec.ts +++ b/packages/form-core/src/tests/FormApi.spec.ts @@ -259,7 +259,7 @@ describe('form api', () => { employees: Partial[] } - const form = new FormApi() + const form = new FormApi() const field = new FieldApi({ form, @@ -287,7 +287,7 @@ describe('form api', () => { employees: Partial[] } - const form = new FormApi() + const form = new FormApi() const field = new FieldApi({ form, diff --git a/packages/react-form/src/createFormFactory.ts b/packages/react-form/src/createFormFactory.ts index 3003574fc..5bd7f2461 100644 --- a/packages/react-form/src/createFormFactory.ts +++ b/packages/react-form/src/createFormFactory.ts @@ -1,23 +1,29 @@ -import type { FormApi, FormOptions } from '@tanstack/form-core' +import type { FormApi, FormOptions, Validator } from '@tanstack/form-core' import { type UseField, type FieldComponent, Field, useField } from './useField' import { useForm } from './useForm' -export type FormFactory = { +export type FormFactory< + TFormData, + TFormValidator extends Validator | undefined = undefined, +> = { useForm: ( - opts?: FormOptions, - ) => FormApi + opts?: FormOptions, + ) => FormApi useField: UseField - Field: FieldComponent + Field: FieldComponent } -export function createFormFactory( - defaultOpts?: FormOptions, -): FormFactory { +export function createFormFactory< + TFormData, + TFormValidator extends Validator | undefined = undefined, +>( + defaultOpts?: FormOptions, +): FormFactory { return { useForm: (opts) => { const formOptions = Object.assign({}, defaultOpts, opts) - return useForm(formOptions) + return useForm(formOptions) }, useField: useField as any, Field: Field as any, diff --git a/packages/react-form/src/formContext.ts b/packages/react-form/src/formContext.ts index c2403b326..57c4742c9 100644 --- a/packages/react-form/src/formContext.ts +++ b/packages/react-form/src/formContext.ts @@ -1,8 +1,8 @@ -import type { FormApi } from '@tanstack/form-core' +import type { FormApi, Validator } from '@tanstack/form-core' import * as React from 'react' export const formContext = React.createContext<{ - formApi: FormApi + formApi: FormApi | undefined> parentFieldName?: string } | null>(null!) diff --git a/packages/react-form/src/tests/createFormFactory.test.tsx b/packages/react-form/src/tests/createFormFactory.test.tsx index 752d6a715..d8648cd21 100644 --- a/packages/react-form/src/tests/createFormFactory.test.tsx +++ b/packages/react-form/src/tests/createFormFactory.test.tsx @@ -11,7 +11,7 @@ describe('createFormFactory', () => { lastName: string } - const formFactory = createFormFactory({ + const formFactory = createFormFactory({ defaultValues: { firstName: 'FirstName', lastName: 'LastName', diff --git a/packages/react-form/src/tests/useField.test.tsx b/packages/react-form/src/tests/useField.test.tsx index 7e095d5c9..5cb306a50 100644 --- a/packages/react-form/src/tests/useField.test.tsx +++ b/packages/react-form/src/tests/useField.test.tsx @@ -15,7 +15,7 @@ describe('useField', () => { lastName: string } - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm({ @@ -55,7 +55,7 @@ describe('useField', () => { lastName: string } - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm({ @@ -97,7 +97,7 @@ describe('useField', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm() @@ -139,7 +139,7 @@ describe('useField', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm() @@ -184,7 +184,7 @@ describe('useField', () => { const onChangeError = 'Please enter a different value (onChangeError)' const onBlurError = 'Please enter a different value (onBlurError)' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm() @@ -235,7 +235,7 @@ describe('useField', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm() @@ -284,7 +284,7 @@ describe('useField', () => { const onChangeError = 'Please enter a different value (onChangeError)' const onBlurError = 'Please enter a different value (onBlurError)' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm() @@ -342,7 +342,7 @@ describe('useField', () => { } const mockFn = vi.fn() const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm() @@ -391,8 +391,8 @@ describe('useField', () => { firstName: string lastName: string } - const formFactory = createFormFactory() - let form: FormApi | null = null + const formFactory = createFormFactory() + let form: FormApi | null = null function Comp() { form = formFactory.useForm() return ( @@ -429,8 +429,8 @@ describe('useField', () => { firstName: string lastName: string } - const formFactory = createFormFactory() - let form: FormApi | null = null + const formFactory = createFormFactory() + let form: FormApi | null = null function Comp() { form = formFactory.useForm() return ( diff --git a/packages/react-form/src/tests/useForm.test.tsx b/packages/react-form/src/tests/useForm.test.tsx index 23398836e..3aaee89e9 100644 --- a/packages/react-form/src/tests/useForm.test.tsx +++ b/packages/react-form/src/tests/useForm.test.tsx @@ -15,7 +15,7 @@ describe('useForm', () => { lastName: string } - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm() @@ -53,7 +53,7 @@ describe('useForm', () => { lastName: string } - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm({ @@ -168,7 +168,7 @@ describe('useForm', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm({ @@ -214,7 +214,7 @@ describe('useForm', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm({ @@ -259,7 +259,7 @@ describe('useForm', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm({ @@ -359,7 +359,7 @@ describe('useForm', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm({ @@ -409,7 +409,7 @@ describe('useForm', () => { const onChangeError = 'Please enter a different value (onChangeError)' const onBlurError = 'Please enter a different value (onBlurError)' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm({ @@ -469,7 +469,7 @@ describe('useForm', () => { } const mockFn = vi.fn() const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.useForm({ diff --git a/packages/react-form/src/types.ts b/packages/react-form/src/types.ts index 81dbe6fdd..ad037a921 100644 --- a/packages/react-form/src/types.ts +++ b/packages/react-form/src/types.ts @@ -1,11 +1,20 @@ -import type { FieldOptions, DeepKeys, DeepValue } from '@tanstack/form-core' +import type { + FieldOptions, + DeepKeys, + DeepValue, + Validator, +} from '@tanstack/form-core' export type UseFieldOptions< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, -> = FieldOptions & { +> = FieldOptions & { mode?: 'value' | 'array' } diff --git a/packages/react-form/src/useField.tsx b/packages/react-form/src/useField.tsx index 60a6bc4fa..63f61adfb 100644 --- a/packages/react-form/src/useField.tsx +++ b/packages/react-form/src/useField.tsx @@ -1,6 +1,11 @@ import React, { useState } from 'react' import { useStore } from '@tanstack/react-store' -import type { DeepKeys, DeepValue, Narrow } from '@tanstack/form-core' +import type { + DeepKeys, + DeepValue, + Narrow, + Validator, +} from '@tanstack/form-core' import { FieldApi, functionalUpdate } from '@tanstack/form-core' import { useFormContext, formContext } from './formContext' import type { UseFieldOptions } from './types' @@ -11,49 +16,53 @@ declare module '@tanstack/form-core' { interface FieldApi< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, > { - Field: FieldComponent + Field: FieldComponent } } export type UseField = < TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, >( opts?: { name: Narrow } & UseFieldOptions< TParentData, TName, - ValidatorType, - FormValidator + TFieldValidator, + TFormValidator >, ) => FieldApi< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, DeepValue > export function useField< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, >( - opts: UseFieldOptions, -): FieldApi< - TParentData, - TName, - ValidatorType, - FormValidator - // Omit & { - // form: FormApi - // } -> { + opts: UseFieldOptions, +): FieldApi { // Get the form API either manually or from context const { formApi, parentFieldName } = useFormContext() @@ -68,7 +77,7 @@ export function useField< const api = new FieldApi({ ...opts, - form: formApi, + form: formApi as never, // TODO: Fix typings to include `index` and `parentFieldName`, if present name: name as typeof opts.name as never, }) @@ -103,12 +112,22 @@ export function useField< type FieldComponentProps< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, > = { children: ( - fieldApi: FieldApi, + fieldApi: FieldApi< + TParentData, + TName, + TFieldValidator, + TFormValidator, + TData + >, ) => any } & (TParentData extends any[] ? { @@ -120,13 +139,20 @@ type FieldComponentProps< index?: never }) & Omit< - UseFieldOptions, + UseFieldOptions, 'name' | 'index' > -export type FieldComponent = < +export type FieldComponent< + TParentData, + TFormValidator extends + | Validator + | undefined = undefined, +> = < TName extends DeepKeys, - ValidatorType, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, TData extends DeepValue = DeepValue, >({ children, @@ -134,24 +160,28 @@ export type FieldComponent = < }: FieldComponentProps< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData >) => any export function Field< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, >({ children, ...fieldOptions }: { children: ( - fieldApi: FieldApi, + fieldApi: FieldApi, ) => any -} & UseFieldOptions) { +} & UseFieldOptions) { const fieldApi = useField(fieldOptions as any) return ( diff --git a/packages/react-form/src/useForm.tsx b/packages/react-form/src/useForm.tsx index 5711db884..43d03dc96 100644 --- a/packages/react-form/src/useForm.tsx +++ b/packages/react-form/src/useForm.tsx @@ -1,4 +1,4 @@ -import type { FormState, FormOptions } from '@tanstack/form-core' +import type { FormState, FormOptions, Validator } from '@tanstack/form-core' import { FormApi, functionalUpdate } from '@tanstack/form-core' import type { NoInfer } from '@tanstack/react-store' import { useStore } from '@tanstack/react-store' @@ -9,9 +9,9 @@ import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect' declare module '@tanstack/form-core' { // eslint-disable-next-line no-shadow - interface FormApi { + interface FormApi { Provider: (props: { children: any }) => any - Field: FieldComponent + Field: FieldComponent useField: UseField useStore: >>( selector?: (state: NoInfer>) => TSelected, @@ -23,16 +23,21 @@ declare module '@tanstack/form-core' { } } -export function useForm( - opts?: FormOptions, -): FormApi { +export function useForm< + TFormData, + TFormValidator extends Validator | undefined = undefined, +>( + opts?: FormOptions, +): FormApi { const [formApi] = useState(() => { // @ts-ignore - const api = new FormApi(opts) + const api = new FormApi(opts) api.Provider = function Provider(props) { useIsomorphicLayoutEffect(api.mount, []) - return + return ( + + ) } api.Field = Field as any api.useField = useField as any diff --git a/packages/solid-form/src/createField.tsx b/packages/solid-form/src/createField.tsx index 361eb8f5b..4aa65ba4c 100644 --- a/packages/solid-form/src/createField.tsx +++ b/packages/solid-form/src/createField.tsx @@ -1,4 +1,4 @@ -import { FieldApi } from '@tanstack/form-core' +import { FieldApi, type Validator } from '@tanstack/form-core' import { createComponent, createComputed, @@ -18,30 +18,38 @@ declare module '@tanstack/form-core' { interface FieldApi< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, > { - Field: FieldComponent + Field: FieldComponent } } export type CreateField = < TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, >( opts: () => { name: Narrow } & CreateFieldOptions< TParentData, TName, - ValidatorType, - FormValidator + TFieldValidator, + TFormValidator >, ) => () => FieldApi< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, DeepValue > @@ -59,24 +67,20 @@ function makeFieldReactive>( export function createField< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, >( opts: () => CreateFieldOptions< TParentData, TName, - ValidatorType, - FormValidator + TFieldValidator, + TFormValidator >, -): () => FieldApi< - TParentData, - TName, - ValidatorType, - FormValidator - // Omit & { - // form: FormApi - // } -> { +): () => FieldApi { // Get the form API either manually or from context const { formApi, parentFieldName } = useFormContext() @@ -113,16 +117,20 @@ export function createField< type FieldComponentProps< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, > = { children: ( fieldApi: () => FieldApi< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData >, ) => JSXElement @@ -136,13 +144,20 @@ type FieldComponentProps< index?: never }) & Omit< - CreateFieldOptions, + CreateFieldOptions, 'name' | 'index' > -export type FieldComponent = < +export type FieldComponent< + TParentData, + TFormValidator extends + | Validator + | undefined = undefined, +> = < TName extends DeepKeys, - ValidatorType, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, TData extends DeepValue = DeepValue, >({ children, @@ -150,33 +165,37 @@ export type FieldComponent = < }: FieldComponentProps< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData >) => JSXElement export function Field< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, >( props: { children: ( fieldApi: () => FieldApi< TParentData, TName, - ValidatorType, - FormValidator + TFieldValidator, + TFormValidator >, ) => JSXElement - } & CreateFieldOptions, + } & CreateFieldOptions, ) { const fieldApi = createField< TParentData, TName, - ValidatorType, - FormValidator + TFieldValidator, + TFormValidator >(() => { const { children, ...fieldOptions } = props return fieldOptions @@ -185,7 +204,7 @@ export function Field< return ( diff --git a/packages/solid-form/src/createForm.tsx b/packages/solid-form/src/createForm.tsx index dca7d613b..15b739cd3 100644 --- a/packages/solid-form/src/createForm.tsx +++ b/packages/solid-form/src/createForm.tsx @@ -1,4 +1,4 @@ -import type { FormOptions, FormState } from '@tanstack/form-core' +import type { FormOptions, FormState, Validator } from '@tanstack/form-core' import { FormApi, functionalUpdate } from '@tanstack/form-core' import { createComputed, onMount, type JSXElement } from 'solid-js' import { useStore } from '@tanstack/solid-store' @@ -14,9 +14,9 @@ type NoInfer = [T][T extends any ? 0 : never] declare module '@tanstack/form-core' { // eslint-disable-next-line no-shadow - interface FormApi { + interface FormApi { Provider: (props: { children: any }) => any - Field: FieldComponent + Field: FieldComponent createField: CreateField useStore: >>( selector?: (state: NoInfer>) => TSelected, @@ -28,18 +28,25 @@ declare module '@tanstack/form-core' { } } -export function createForm( - opts?: () => FormOptions, -): FormApi { +export function createForm< + TParentData, + TFormValidator extends + | Validator + | undefined = undefined, +>( + opts?: () => FormOptions, +): FormApi { const options = opts?.() - const formApi = new FormApi(options) + const formApi = new FormApi(options) formApi.Provider = function Provider(props) { onMount(formApi.mount) - return + return ( + + ) } formApi.Field = Field as any - formApi.createField = createField as CreateField + formApi.createField = createField as CreateField formApi.useStore = (selector) => useStore(formApi.store, selector) formApi.Subscribe = (props) => functionalUpdate(props.children, useStore(formApi.store, props.selector)) diff --git a/packages/solid-form/src/createFormFactory.ts b/packages/solid-form/src/createFormFactory.ts index 808e062f0..1a0d3db45 100644 --- a/packages/solid-form/src/createFormFactory.ts +++ b/packages/solid-form/src/createFormFactory.ts @@ -1,4 +1,4 @@ -import type { FormApi, FormOptions } from '@tanstack/form-core' +import type { FormApi, FormOptions, Validator } from '@tanstack/form-core' import { type CreateField, @@ -9,20 +9,26 @@ import { import { createForm } from './createForm' import { mergeProps } from 'solid-js' -export type FormFactory = { +export type FormFactory< + TFormData, + TFormValidator extends Validator | undefined = undefined, +> = { createForm: ( - opts?: () => FormOptions, - ) => FormApi + opts?: () => FormOptions, + ) => FormApi createField: CreateField - Field: FieldComponent + Field: FieldComponent } -export function createFormFactory( - defaultOpts?: () => FormOptions, -): FormFactory { +export function createFormFactory< + TFormData, + TFormValidator extends Validator | undefined = undefined, +>( + defaultOpts?: () => FormOptions, +): FormFactory { return { createForm: (opts) => - createForm(() => + createForm(() => mergeProps(defaultOpts?.() ?? {}, opts?.() ?? {}), ), createField, diff --git a/packages/solid-form/src/formContext.ts b/packages/solid-form/src/formContext.ts index ef2b21f0e..8408b8086 100644 --- a/packages/solid-form/src/formContext.ts +++ b/packages/solid-form/src/formContext.ts @@ -1,10 +1,10 @@ import { createContext, useContext } from 'solid-js' -import type { FormApi } from '@tanstack/form-core' +import type { FormApi, Validator } from '@tanstack/form-core' type FormContextType = | undefined | { - formApi: FormApi + formApi: FormApi | undefined> parentFieldName?: string } diff --git a/packages/solid-form/src/tests/createField.test.tsx b/packages/solid-form/src/tests/createField.test.tsx index bfe499a22..28af21614 100644 --- a/packages/solid-form/src/tests/createField.test.tsx +++ b/packages/solid-form/src/tests/createField.test.tsx @@ -13,7 +13,7 @@ describe('createField', () => { lastName: string } - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm() @@ -49,7 +49,7 @@ describe('createField', () => { lastName: string } - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm(() => ({ @@ -91,7 +91,7 @@ describe('createField', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm() @@ -134,7 +134,7 @@ describe('createField', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm() @@ -182,7 +182,7 @@ describe('createField', () => { const onChangeError = 'Please enter a different value (onChangeError)' const onBlurError = 'Please enter a different value (onBlurError)' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm() @@ -234,7 +234,7 @@ describe('createField', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm() @@ -283,7 +283,7 @@ describe('createField', () => { const onChangeError = 'Please enter a different value (onChangeError)' const onBlurError = 'Please enter a different value (onBlurError)' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm() @@ -342,7 +342,7 @@ describe('createField', () => { } const mockFn = vi.fn() const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm() diff --git a/packages/solid-form/src/tests/createForm.test.tsx b/packages/solid-form/src/tests/createForm.test.tsx index 7cac2aa67..6f4525a8c 100644 --- a/packages/solid-form/src/tests/createForm.test.tsx +++ b/packages/solid-form/src/tests/createForm.test.tsx @@ -15,7 +15,7 @@ describe('createForm', () => { lastName: string } - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm() @@ -50,7 +50,7 @@ describe('createForm', () => { lastName: string } - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm(() => ({ @@ -159,7 +159,7 @@ describe('createForm', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm(() => ({ @@ -203,7 +203,7 @@ describe('createForm', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm(() => ({ @@ -255,7 +255,7 @@ describe('createForm', () => { const onChangeError = 'Please enter a different value (onChangeError)' const onBlurError = 'Please enter a different value (onBlurError)' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm(() => ({ @@ -311,7 +311,7 @@ describe('createForm', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm(() => ({ @@ -364,7 +364,7 @@ describe('createForm', () => { const onChangeError = 'Please enter a different value (onChangeError)' const onBlurError = 'Please enter a different value (onBlurError)' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm(() => ({ @@ -427,7 +427,7 @@ describe('createForm', () => { } const mockFn = vi.fn() const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() function Comp() { const form = formFactory.createForm(() => ({ diff --git a/packages/solid-form/src/tests/createFormFactory.test.tsx b/packages/solid-form/src/tests/createFormFactory.test.tsx index c4d2fee8b..232796b61 100644 --- a/packages/solid-form/src/tests/createFormFactory.test.tsx +++ b/packages/solid-form/src/tests/createFormFactory.test.tsx @@ -9,7 +9,7 @@ describe('createFormFactory', () => { lastName: string } - const formFactory = createFormFactory(() => ({ + const formFactory = createFormFactory(() => ({ defaultValues: { firstName: 'FirstName', lastName: 'LastName', diff --git a/packages/solid-form/src/types.ts b/packages/solid-form/src/types.ts index cd1bcd8d7..d64b25104 100644 --- a/packages/solid-form/src/types.ts +++ b/packages/solid-form/src/types.ts @@ -1,11 +1,20 @@ -import type { FieldOptions, DeepKeys, DeepValue } from '@tanstack/form-core' +import type { + FieldOptions, + DeepKeys, + DeepValue, + Validator, +} from '@tanstack/form-core' export type CreateFieldOptions< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, -> = FieldOptions & { +> = FieldOptions & { mode?: 'value' | 'array' } diff --git a/packages/valibot-form-adapter/src/tests/FieldApi.spec.ts b/packages/valibot-form-adapter/src/tests/FieldApi.spec.ts index 55ad5f6fa..388f31252 100644 --- a/packages/valibot-form-adapter/src/tests/FieldApi.spec.ts +++ b/packages/valibot-form-adapter/src/tests/FieldApi.spec.ts @@ -102,7 +102,8 @@ describe('valibot field api', () => { validatorAdapter: valibotValidator, name: 'name', validators: { - onChangeAsync: async ({ value }) => (value === 'a' ? 'Test' : undefined), + onChangeAsync: async ({ value }) => + value === 'a' ? 'Test' : undefined, onChangeAsyncDebounceMs: 0, }, }) diff --git a/packages/valibot-form-adapter/src/validator.ts b/packages/valibot-form-adapter/src/validator.ts index c33c7c2e3..2cdd9348f 100644 --- a/packages/valibot-form-adapter/src/validator.ts +++ b/packages/valibot-form-adapter/src/validator.ts @@ -2,11 +2,12 @@ import { safeParse, safeParseAsync } from 'valibot' import type { BaseSchema, BaseSchemaAsync } from 'valibot' import type { ValidationError, Validator } from '@tanstack/form-core' -export const valibotValidator = (< - Fn extends BaseSchema | BaseSchemaAsync = BaseSchema | BaseSchemaAsync, ->() => { +export const valibotValidator = (() => { return { - validate({ value }: { value: unknown }, fn: Fn): ValidationError { + validate( + { value }: { value: unknown }, + fn: BaseSchema | BaseSchemaAsync, + ): ValidationError { if (fn.async) return const result = safeParse(fn, value) if (result.success) return @@ -14,7 +15,7 @@ export const valibotValidator = (< }, async validateAsync( { value }: { value: unknown }, - fn: Fn, + fn: BaseSchema | BaseSchemaAsync, ): Promise { const result = await safeParseAsync(fn, value) if (result.success) return diff --git a/packages/vue-form/src/createFormFactory.ts b/packages/vue-form/src/createFormFactory.ts index f0c351c9c..4abd8c00c 100644 --- a/packages/vue-form/src/createFormFactory.ts +++ b/packages/vue-form/src/createFormFactory.ts @@ -1,23 +1,29 @@ -import type { FormApi, FormOptions } from '@tanstack/form-core' +import type { FormApi, FormOptions, Validator } from '@tanstack/form-core' import { type UseField, type FieldComponent, Field, useField } from './useField' import { useForm } from './useForm' -export type FormFactory = { +export type FormFactory< + TFormData, + TFormValidator extends Validator | undefined = undefined, +> = { useForm: ( - opts?: FormOptions, - ) => FormApi - useField: UseField - Field: FieldComponent + opts?: FormOptions, + ) => FormApi + useField: UseField + Field: FieldComponent } -export function createFormFactory( - defaultOpts?: FormOptions, -): FormFactory { +export function createFormFactory< + TFormData, + TFormValidator extends Validator | undefined = undefined, +>( + defaultOpts?: FormOptions, +): FormFactory { return { useForm: (opts) => { const formOptions = Object.assign({}, defaultOpts, opts) - return useForm(formOptions) + return useForm(formOptions) }, useField: useField as any, Field: Field as any, diff --git a/packages/vue-form/src/formContext.ts b/packages/vue-form/src/formContext.ts index 4faa604f9..8754a64d6 100644 --- a/packages/vue-form/src/formContext.ts +++ b/packages/vue-form/src/formContext.ts @@ -1,14 +1,20 @@ -import type { FormApi } from '@tanstack/form-core' +import type { FormApi, Validator } from '@tanstack/form-core' import { inject, provide } from 'vue' -export type FormContext = { - formApi: FormApi +export type FormContext< + TFormData = any, + TFormValidator extends Validator | undefined = undefined, +> = { + formApi: FormApi parentFieldName?: string } | null export const formContext = Symbol('FormContext') -export function provideFormContext(val: FormContext) { +export function provideFormContext< + TFormData = any, + TFormValidator extends Validator | undefined = undefined, +>(val: FormContext) { provide(formContext, val) } diff --git a/packages/vue-form/src/tests/useField.test.tsx b/packages/vue-form/src/tests/useField.test.tsx index fff397062..3e9d8f880 100644 --- a/packages/vue-form/src/tests/useField.test.tsx +++ b/packages/vue-form/src/tests/useField.test.tsx @@ -15,7 +15,7 @@ describe('useField', () => { lastName: string } - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm() @@ -54,7 +54,7 @@ describe('useField', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm() @@ -103,7 +103,7 @@ describe('useField', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm() @@ -153,7 +153,7 @@ describe('useField', () => { } const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm() @@ -209,7 +209,7 @@ describe('useField', () => { const mockFn = vi.fn() const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm() diff --git a/packages/vue-form/src/tests/useForm.test.tsx b/packages/vue-form/src/tests/useForm.test.tsx index 577444226..ae59339d6 100644 --- a/packages/vue-form/src/tests/useForm.test.tsx +++ b/packages/vue-form/src/tests/useForm.test.tsx @@ -22,7 +22,7 @@ type Person = { describe('useForm', () => { it('preserved field state', async () => { - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm() @@ -57,7 +57,7 @@ describe('useForm', () => { }) it('should allow default values to be set', async () => { - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm({ @@ -173,7 +173,7 @@ describe('useForm', () => { it('should validate async on change for the form', async () => { const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm({ @@ -222,7 +222,7 @@ describe('useForm', () => { it('should not validate on change if isTouched is false', async () => { const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm({ @@ -271,7 +271,7 @@ describe('useForm', () => { it('should validate on change if isTouched is true', async () => { const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm({ @@ -382,7 +382,7 @@ describe('useForm', () => { it('should validate async on change', async () => { const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm({ @@ -436,7 +436,7 @@ describe('useForm', () => { const onChangeError = 'Please enter a different value (onChangeError)' const onBlurError = 'Please enter a different value (onBlurError)' - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm({ @@ -498,7 +498,7 @@ describe('useForm', () => { it('should validate async on change with debounce', async () => { const mockFn = vi.fn() const error = 'Please enter a different value' - const formFactory = createFormFactory() + const formFactory = createFormFactory() const Comp = defineComponent(() => { const form = formFactory.useForm({ diff --git a/packages/vue-form/src/types.ts b/packages/vue-form/src/types.ts index 81dbe6fdd..ad037a921 100644 --- a/packages/vue-form/src/types.ts +++ b/packages/vue-form/src/types.ts @@ -1,11 +1,20 @@ -import type { FieldOptions, DeepKeys, DeepValue } from '@tanstack/form-core' +import type { + FieldOptions, + DeepKeys, + DeepValue, + Validator, +} from '@tanstack/form-core' export type UseFieldOptions< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, -> = FieldOptions & { +> = FieldOptions & { mode?: 'value' | 'array' } diff --git a/packages/vue-form/src/useField.tsx b/packages/vue-form/src/useField.tsx index 27dbc7198..e0242871b 100644 --- a/packages/vue-form/src/useField.tsx +++ b/packages/vue-form/src/useField.tsx @@ -1,4 +1,4 @@ -import { FieldApi } from '@tanstack/form-core' +import { FieldApi, type Validator } from '@tanstack/form-core' import type { DeepKeys, DeepValue, Narrow } from '@tanstack/form-core' import { useStore } from '@tanstack/vue-store' import { defineComponent, onMounted, onUnmounted, watch } from 'vue' @@ -11,53 +11,68 @@ declare module '@tanstack/form-core' { interface FieldApi< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData = DeepValue, > { - Field: FieldComponent + Field: FieldComponent } } -export type UseField = < +export type UseField< + TParentData, + TFormValidator extends + | Validator + | undefined = undefined, +> = < TName extends DeepKeys, - ValidatorType, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, >( opts?: { name: Narrow } & UseFieldOptions< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, DeepValue >, ) => FieldApi< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, DeepValue > export function useField< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, TData extends DeepValue = DeepValue, >( opts: UseFieldOptions< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData >, ): { api: FieldApi< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData // Omit & { // form: FormApi @@ -68,12 +83,9 @@ export function useField< FieldApi< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData - // Omit & { - // form: FormApi - // } >['state'] > > @@ -124,8 +136,12 @@ export type FieldValue = TParentData extends any[] type FieldComponentProps< TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, > = (TParentData extends any[] ? { name?: TName @@ -136,31 +152,44 @@ type FieldComponentProps< index?: never }) & Omit< - UseFieldOptions, + UseFieldOptions, 'name' | 'index' > -export type FieldComponent = < +export type FieldComponent< + TParentData, + TFormValidator extends + | Validator + | undefined = undefined, +> = < TName extends DeepKeys, - ValidatorType, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, TData extends DeepValue = DeepValue, >( fieldOptions: FieldComponentProps< TParentData, TName, - ValidatorType, - FormValidator + TFieldValidator, + TFormValidator >, context: SetupContext< {}, SlotsType<{ default: { - field: FieldApi + field: FieldApi< + TParentData, + TName, + TFieldValidator, + TFormValidator, + TData + > state: FieldApi< TParentData, TName, - ValidatorType, - FormValidator, + TFieldValidator, + TFormValidator, TData >['state'] } @@ -172,14 +201,18 @@ export const Field = defineComponent( < TParentData, TName extends DeepKeys, - ValidatorType, - FormValidator, + TFieldValidator extends + | Validator, unknown> + | undefined = undefined, + TFormValidator extends + | Validator + | undefined = undefined, >( fieldOptions: UseFieldOptions< TParentData, TName, - ValidatorType, - FormValidator + TFieldValidator, + TFormValidator >, context: SetupContext, ) => { diff --git a/packages/vue-form/src/useForm.tsx b/packages/vue-form/src/useForm.tsx index 50b267eed..4ffc040dd 100644 --- a/packages/vue-form/src/useForm.tsx +++ b/packages/vue-form/src/useForm.tsx @@ -1,4 +1,9 @@ -import { FormApi, type FormState, type FormOptions } from '@tanstack/form-core' +import { + FormApi, + type FormState, + type FormOptions, + type Validator, +} from '@tanstack/form-core' import { type NoInfer, useStore } from '@tanstack/vue-store' import { type UseField, type FieldComponent, Field, useField } from './useField' import { provideFormContext } from './formContext' @@ -13,11 +18,11 @@ import { declare module '@tanstack/form-core' { // eslint-disable-next-line no-shadow - interface FormApi { + interface FormApi { Provider: (props: Record & {}) => any provideFormContext: () => void - Field: FieldComponent - useField: UseField + Field: FieldComponent + useField: UseField useStore: >>( selector?: (state: NoInfer>) => TSelected, ) => Readonly> @@ -33,11 +38,14 @@ declare module '@tanstack/form-core' { } } -export function useForm( - opts?: FormOptions, -): FormApi { +export function useForm< + TFormData, + TFormValidator extends Validator | undefined = undefined, +>( + opts?: FormOptions, +): FormApi { const formApi = (() => { - const api = new FormApi(opts) + const api = new FormApi(opts) api.Provider = defineComponent( (_, context) => { diff --git a/packages/yup-form-adapter/src/tests/FieldApi.spec.ts b/packages/yup-form-adapter/src/tests/FieldApi.spec.ts index 8d88ae083..d4900bfc8 100644 --- a/packages/yup-form-adapter/src/tests/FieldApi.spec.ts +++ b/packages/yup-form-adapter/src/tests/FieldApi.spec.ts @@ -102,7 +102,8 @@ describe('yup field api', () => { validatorAdapter: yupValidator, name: 'name', validators: { - onChangeAsync: async ({ value }) => (value === 'a' ? 'Test' : undefined), + onChangeAsync: async ({ value }) => + value === 'a' ? 'Test' : undefined, onChangeAsyncDebounceMs: 0, }, }) diff --git a/packages/yup-form-adapter/src/validator.ts b/packages/yup-form-adapter/src/validator.ts index 7513b5ab5..9b981fba4 100644 --- a/packages/yup-form-adapter/src/validator.ts +++ b/packages/yup-form-adapter/src/validator.ts @@ -1,9 +1,9 @@ import type { ValidationError as YupError, AnySchema } from 'yup' import type { ValidationError, Validator } from '@tanstack/form-core' -export const yupValidator = (() => { +export const yupValidator = (() => { return { - validate({ value }: { value: unknown }, fn: Fn): ValidationError { + validate({ value }: { value: unknown }, fn: AnySchema): ValidationError { try { fn.validateSync(value) return @@ -14,7 +14,7 @@ export const yupValidator = (() => { }, async validateAsync( { value }: { value: unknown }, - fn: Fn, + fn: AnySchema, ): Promise { try { await fn.validate(value) diff --git a/packages/zod-form-adapter/src/tests/FieldApi.spec.ts b/packages/zod-form-adapter/src/tests/FieldApi.spec.ts index 0e223f1d8..e7c9966e7 100644 --- a/packages/zod-form-adapter/src/tests/FieldApi.spec.ts +++ b/packages/zod-form-adapter/src/tests/FieldApi.spec.ts @@ -100,7 +100,8 @@ describe('zod field api', () => { validatorAdapter: zodValidator, name: 'name', validators: { - onChangeAsync: async ({ value }) => (value === 'a' ? 'Test' : undefined), + onChangeAsync: async ({ value }) => + value === 'a' ? 'Test' : undefined, onChangeAsyncDebounceMs: 0, }, }) diff --git a/packages/zod-form-adapter/src/validator.ts b/packages/zod-form-adapter/src/validator.ts index 92f3c4190..95b24e32e 100644 --- a/packages/zod-form-adapter/src/validator.ts +++ b/packages/zod-form-adapter/src/validator.ts @@ -1,9 +1,9 @@ import type { ZodType, ZodTypeAny } from 'zod' import type { ValidationError, Validator } from '@tanstack/form-core' -export const zodValidator = (() => { +export const zodValidator = (() => { return { - validate({ value }: { value: unknown }, fn: Fn): ValidationError { + validate({ value }: { value: unknown }, fn: ZodType): ValidationError { // Call Zod on the value here and return the error message const result = (fn as ZodTypeAny).safeParse(value) if (!result.success) { @@ -13,7 +13,7 @@ export const zodValidator = (() => { }, async validateAsync( { value }: { value: unknown }, - fn: Fn, + fn: ZodType, ): Promise { // Call Zod on the value here and return the error message const result = await (fn as ZodTypeAny).safeParseAsync(value)