diff --git a/docs/framework/react/reference/Field.md b/docs/framework/react/reference/Field.md index 3c06338..247606d 100644 --- a/docs/framework/react/reference/Field.md +++ b/docs/framework/react/reference/Field.md @@ -3,18 +3,23 @@ id: field title: Field --- -### `FieldComponent` +### `FieldComponent` A type alias representing a field component for a specific form data type. ```tsx -export type FieldComponent = >({ +export type FieldComponent = >({ children, ...fieldOptions }: { - children: (fieldApi: FieldApi, TFormData>) => any + children: ( + fieldApi: FieldApi, TParentData>, + ) => any name: TField -} & Omit, TFormData>, 'name'>) => any +} & Omit< + FieldOptions, TParentData>, + 'name' +>) => any ``` A function component that takes field options and a render function as children and returns a React component. @@ -22,23 +27,23 @@ A function component that takes field options and a render function as children ### `Field` ```tsx -export function Field({ +export function Field({ children, ...fieldOptions -}: { children: (fieldApi: FieldApi) => any } & FieldOptions< +}: { children: (fieldApi: FieldApi) => any } & FieldOptions< TData, - TFormData + TParentData >): any ``` A functional React component that renders a form field. - ```tsx - children: (fieldApi: FieldApi) => any + children: (fieldApi: FieldApi) => any ``` - A render function that takes a field API instance and returns a React element. - ```tsx - fieldOptions: FieldOptions + fieldOptions: FieldOptions ``` - The field options. @@ -47,14 +52,14 @@ The `Field` component uses the `useField` hook internally to manage the field in ### `createFieldComponent` ```tsx -export function createFieldComponent( - formApi: FormApi, -): FieldComponent +export function createFieldComponent( + formApi: FormApi, +): FieldComponent ``` A factory function that creates a connected field component for a specific form API instance. - ```tsx - formApi: FormApi + formApi: FormApi ``` - The form API instance to connect the field component to. diff --git a/docs/framework/react/reference/fieldApi.md b/docs/framework/react/reference/fieldApi.md index 494f27d..8a2422d 100644 --- a/docs/framework/react/reference/fieldApi.md +++ b/docs/framework/react/reference/fieldApi.md @@ -3,11 +3,11 @@ id: fieldApi title: Field API --- -### `FieldApi` +### `FieldApi` When using `@tanstack/react-form`, the [core field API](../../reference/fieldApi) is extended with additional methods for React-specific functionality: - ```tsx - Field: FieldComponent + Field: FieldComponent ``` - A pre-bound and type-safe sub-field component using this field as a root. diff --git a/docs/framework/react/reference/useField.md b/docs/framework/react/reference/useField.md index c308266..3471855 100644 --- a/docs/framework/react/reference/useField.md +++ b/docs/framework/react/reference/useField.md @@ -3,17 +3,17 @@ id: useField title: useField --- -### `UseField` +### `UseField` A type representing a hook for using a field in a form with the given form data type. ```tsx -export type UseField = >( +export type UseField = >( opts?: { name: TField } & FieldOptions< - DeepValue, - TFormData + DeepValue, + TParentData >, -) => FieldApi, TFormData> +) => FieldApi, TParentData> ``` - A function that takes an optional object with a `name` property and field options, and returns a `FieldApi` instance for the specified field. @@ -21,31 +21,31 @@ export type UseField = >( ### `useField` ```tsx -export function useField( - opts: FieldOptions, -): FieldApi +export function useField( + opts: FieldOptions, +): FieldApi ``` A hook for managing a field in a form. - ```tsx - opts: FieldOptions + opts: FieldOptions ``` - An object with field options. #### Returns - ```tsx - FieldApi + FieldApi ``` - The `FieldApi` instance for the specified field. ### `createUseField` ```tsx -export function createUseField( - formApi: FormApi, -): UseField +export function createUseField( + formApi: FormApi, +): UseField ``` A function that creates a `UseField` hook bound to the given `formApi`. diff --git a/docs/reference/fieldApi.md b/docs/reference/fieldApi.md index 71fbd3a..9934fa4 100644 --- a/docs/reference/fieldApi.md +++ b/docs/reference/fieldApi.md @@ -11,14 +11,14 @@ Normally, you will not need to create a new `FieldApi` instance directly. Instea const fieldApi: FieldApi = new FieldApi(formOptions: Field Options) ``` -### `FieldOptions` +### `FieldOptions` An object type representing the options for a field in a form. - ```tsx name ``` - - The field name. If `TFormData` is `unknown`, the type will be `string`. Otherwise, it will be `DeepKeys`. + - The field name. If `TParentData` is `unknown`, the type will be `string`. Otherwise, it will be `DeepKeys`. - ```tsx defaultValue?: TData ``` @@ -30,19 +30,19 @@ An object type representing the options for a field in a form. - An optional object with default metadata for the field. - ```tsx - onMount?: (formApi: FieldApi) => void + onMount?: (formApi: FieldApi) => void ``` - - An optional function that takes a param of `formApi` which is a generic type of `TData` and `TFormData` + - An optional function that takes a param of `formApi` which is a generic type of `TData` and `TParentData` - ```tsx - onChange?: ValidateFn + onChange?: ValidateFn ``` - - An optional property that takes a `ValidateFn` which is a generic of `TData` and `TFormData` + - An optional property that takes a `ValidateFn` which is a generic of `TData` and `TParentData` - ```tsx - onChangeAsync?: ValidateAsyncFn + onChangeAsync?: ValidateAsyncFn ``` - An optional property similar to `onChange` but async validation @@ -55,16 +55,16 @@ An object type representing the options for a field in a form. - If set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds - ```tsx - onBlur?: ValidateFn + onBlur?: ValidateFn ``` - An optional function, when that run when subscribing to blur event of input - ```tsx - onBlurAsync?: ValidateAsyncFn + onBlurAsync?: ValidateAsyncFn ``` - - An optional function that takes a `ValidateFn` which is a generic of `TData` and `TFormData` happens async + - An optional function that takes a `ValidateFn` which is a generic of `TData` and `TParentData` happens async ```tsx onBlurAsyncDebounceMs?: number @@ -110,13 +110,13 @@ An object type representing the metadata of a field in a form. ``` - A flag indicating whether the field is currently being validated. -### `FieldApiOptions` +### `FieldApiOptions` An object type representing the required options for the `FieldApi` class. -- Inherits from `FieldOptions` with the `form` property set as required. +- Inherits from `FieldOptions` with the `form` property set as required. -### `FieldApi` +### `FieldApi` A class representing the API for managing a form field. @@ -127,11 +127,11 @@ A class representing the API for managing a form field. ``` - A unique identifier for the field instance. - ```tsx - form: FormApi + form: FormApi ``` - A reference to the form API instance. - ```tsx - name: DeepKeys + name: DeepKeys ``` - The field name. - ```tsx @@ -143,14 +143,14 @@ A class representing the API for managing a form field. ``` - The current field state. - ```tsx - options: RequiredByKey, 'validateOn'> + options: RequiredByKey, 'validateOn'> ``` - The field options with the `validateOn` property set as required. #### Methods - ```tsx - constructor(opts: FieldApiOptions) + constructor(opts: FieldApiOptions) ``` - Initializes a new `FieldApi` instance. - ```tsx @@ -162,7 +162,7 @@ A class representing the API for managing a form field. ``` - Updates the field store with the latest form state. - ```tsx - update(opts: FieldApiOptions): void + update(opts: FieldApiOptions): void ``` - Updates the field instance with new options. - ```tsx @@ -202,7 +202,7 @@ A class representing the API for managing a form field. ``` - Swaps the values at the specified indices. - ```tsx - getSubField>(name: TName): FieldApi, TFormData> + getSubField>(name: TName): FieldApi, TParentData> ``` - Gets a subfield instance. - ```tsx diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index 2ed5547..ca78595 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -4,59 +4,67 @@ import { Store } from '@tanstack/store' export type ValidationCause = 'change' | 'blur' | 'submit' | 'mount' -type ValidateFn = ( +type ValidateFn> = ( value: TData, - fieldApi: FieldApi, + fieldApi: FieldApi, ) => ValidationError -type ValidateAsyncFn = ( +type ValidateAsyncFn< + TData, + TParentData, + TName extends DeepKeys, +> = ( value: TData, - fieldApi: FieldApi, + fieldApi: FieldApi, ) => ValidationError | Promise export interface FieldOptions< - _TData, - TFormData, + TData, + TParentData, /** * This allows us to restrict the name to only be a valid field name while * also assigning it to a generic */ - TName = unknown extends TFormData ? string : DeepKeys, + TName extends DeepKeys, /** * If TData is unknown, we can use the TName generic to determine the type */ - TData = unknown extends _TData ? DeepValue : _TData, + TResolvedData = unknown extends TData ? DeepValue : TData, > { - name: TName - index?: TData extends any[] ? number : never - defaultValue?: TData + name: DeepKeys + index?: TResolvedData extends any[] ? number : never + defaultValue?: TResolvedData asyncDebounceMs?: number asyncAlways?: boolean - onMount?: (formApi: FieldApi) => void - onChange?: ValidateFn - onChangeAsync?: ValidateAsyncFn + onMount?: (formApi: FieldApi) => void + onChange?: ValidateFn + onChangeAsync?: ValidateAsyncFn onChangeAsyncDebounceMs?: number - onBlur?: ValidateFn - onBlurAsync?: ValidateAsyncFn + onBlur?: ValidateFn + onBlurAsync?: ValidateAsyncFn onBlurAsyncDebounceMs?: number - onSubmitAsync?: ValidateAsyncFn + onSubmitAsync?: ValidateAsyncFn defaultMeta?: Partial } export interface FieldApiOptions< - _TData, - TFormData, + TData, + TParentData, /** * This allows us to restrict the name to only be a valid field name while * also assigning it to a generic */ - TName = unknown extends TFormData ? string : DeepKeys, + TName extends DeepKeys, /** * If TData is unknown, we can use the TName generic to determine the type */ - TData = unknown extends _TData ? DeepValue : _TData, -> extends FieldOptions<_TData, TFormData, TName, TData> { - form: FormApi + TResolvedData extends ResolveData = ResolveData< + TData, + TParentData, + TName + >, +> extends FieldOptions { + form: FormApi } export type FieldMeta = { @@ -74,43 +82,35 @@ export type FieldState = { meta: FieldMeta } -type GetTData< - TData, - TFormData, - Opts extends FieldApiOptions, -> = Opts extends FieldApiOptions< - infer _TData, - infer _TFormData, - infer _TName, - infer RealTData -> - ? RealTData - : never +export type ResolveData = unknown extends TData + ? DeepValue + : TData + +export type ResolveName = unknown extends TParentData + ? string + : DeepKeys export class FieldApi< - _TData, - TFormData, - Opts extends FieldApiOptions<_TData, TFormData> = FieldApiOptions< - _TData, - TFormData - >, - TData extends GetTData<_TData, TFormData, Opts> = GetTData< - _TData, - TFormData, - Opts + TData, + TParentData, + TName extends DeepKeys, + TResolvedData extends ResolveData = ResolveData< + TData, + TParentData, + TName >, > { uid: number - form: Opts['form'] - name!: DeepKeys - options: Opts = {} as any - store!: Store> - state!: FieldState - prevState!: FieldState + form: FieldApiOptions['form'] + name!: DeepKeys + options: FieldApiOptions = {} as any + store!: Store> + state!: FieldState + prevState!: FieldState constructor( - opts: Opts & { - form: FormApi + opts: FieldApiOptions & { + form: FormApi }, ) { this.form = opts.form @@ -123,7 +123,7 @@ export class FieldApi< this.name = opts.name as any - this.store = new Store>( + this.store = new Store>( { value: this.getValue(), // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition @@ -190,12 +190,12 @@ export class FieldApi< } } - update = (opts: FieldApiOptions) => { + update = (opts: FieldApiOptions) => { // Default Value // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (this.state.value === undefined) { const formDefault = - opts.form.options.defaultValues?.[opts.name as keyof TFormData] + opts.form.options.defaultValues?.[opts.name as keyof TParentData] if (opts.defaultValue !== undefined) { this.setValue(opts.defaultValue as never) @@ -212,12 +212,12 @@ export class FieldApi< this.options = opts as never } - getValue = (): TData => { - return this.form.getFieldValue(this.name) + getValue = (): TResolvedData => { + return this.form.getFieldValue(this.name) as any } setValue = ( - updater: Updater, + updater: Updater, options?: { touch?: boolean; notify?: boolean }, ) => { this.form.setFieldValue(this.name, updater as never, options) @@ -241,12 +241,13 @@ export class FieldApi< getInfo = () => this.form.getFieldInfo(this.name) - pushValue = (value: TData extends any[] ? TData[number] : never) => - this.form.pushFieldValue(this.name, value as any) + pushValue = ( + value: TResolvedData extends any[] ? TResolvedData[number] : never, + ) => this.form.pushFieldValue(this.name, value as any) insertValue = ( index: number, - value: TData extends any[] ? TData[number] : never, + value: TResolvedData extends any[] ? TResolvedData[number] : never, ) => this.form.insertFieldValue(this.name, index, value as any) removeValue = (index: number) => this.form.removeFieldValue(this.name, index) @@ -254,11 +255,21 @@ export class FieldApi< swapValues = (aIndex: number, bIndex: number) => this.form.swapFieldValues(this.name, aIndex, bIndex) - getSubField = >(name: TName) => - new FieldApi, TFormData>({ + getSubField = < + TSubData, + TSubName extends DeepKeys, + TSubResolvedData extends ResolveData< + DeepValue, + TResolvedData, + TSubName + >, + >( + name: TSubName, + ): FieldApi => + new FieldApi({ name: `${this.name}.${name}` as never, form: this.form, - }) + }) as any validateSync = (value = this.state.value, cause: ValidationCause) => { const { onChange, onBlur } = this.options @@ -387,7 +398,7 @@ export class FieldApi< validate = ( cause: ValidationCause, - value?: TData, + value?: TResolvedData, ): ValidationError[] | Promise => { // If the field is pristine and validatePristine is false, do not validate if (!this.state.meta.isTouched) return [] @@ -405,7 +416,7 @@ export class FieldApi< return this.validateAsync(value, cause) } - handleChange = (updater: Updater) => { + handleChange = (updater: Updater) => { this.setValue(updater, { touch: true }) } diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 4e48d57..d348709 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -31,7 +31,7 @@ export type FormOptions = { } export type FieldInfo = { - instances: Record> + instances: Record> } & ValidationMeta export type ValidationMeta = { @@ -106,7 +106,7 @@ export class FormApi { constructor(opts?: FormOptions) { this.store = new Store>( getDefaultFormState({ - ...opts?.defaultState, + ...(opts?.defaultState as any), values: opts?.defaultValues ?? opts?.defaultState?.values, isFormValid: true, }), @@ -174,7 +174,7 @@ export class FormApi { getDefaultFormState( Object.assign( {}, - this.state, + this.state as any, // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition shouldUpdateState ? options.defaultState : {}, // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition @@ -194,7 +194,7 @@ export class FormApi { reset = () => this.store.setState(() => getDefaultFormState({ - ...this.options.defaultState, + ...(this.options.defaultState as any), values: this.options.defaultValues ?? this.options.defaultState?.values, }), ) @@ -288,7 +288,9 @@ export class FormApi { return this.state.fieldMeta[field] } - getFieldInfo = >(field: TField) => { + getFieldInfo = >( + field: TField, + ): FieldInfo => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition return (this.fieldInfo[field] ||= { instances: {}, diff --git a/packages/form-core/src/utils.ts b/packages/form-core/src/utils.ts index 1abd504..e7cd31a 100644 --- a/packages/form-core/src/utils.ts +++ b/packages/form-core/src/utils.ts @@ -133,16 +133,16 @@ type AllowedIndexes< ? AllowedIndexes : Keys -export type DeepKeys = TDepth['length'] extends 10 +export type DeepKeys = TDepth['length'] extends 5 ? never : unknown extends T - ? keyof T + ? string : object extends T ? string : T extends readonly any[] & IsTuple ? AllowedIndexes | DeepKeysPrefix, TDepth> : T extends any[] - ? DeepKeys + ? DeepKeys : T extends Date ? never : T extends object diff --git a/packages/react-form/src/createFormFactory.ts b/packages/react-form/src/createFormFactory.ts index e5e3547..44113cc 100644 --- a/packages/react-form/src/createFormFactory.ts +++ b/packages/react-form/src/createFormFactory.ts @@ -6,7 +6,7 @@ import { useForm } from './useForm' export type FormFactory = { useForm: (opts?: FormOptions) => FormApi useField: UseField - Field: FieldComponent + Field: FieldComponent } export function createFormFactory( diff --git a/packages/react-form/src/types.ts b/packages/react-form/src/types.ts index c8eb121..08068a4 100644 --- a/packages/react-form/src/types.ts +++ b/packages/react-form/src/types.ts @@ -2,8 +2,8 @@ import type { FieldOptions, DeepKeys } from '@tanstack/form-core' export type UseFieldOptions< TData, - TFormData, - TName = unknown extends TFormData ? string : DeepKeys, -> = FieldOptions & { + TParentData, + TName extends DeepKeys, +> = FieldOptions & { mode?: 'value' | 'array' } diff --git a/packages/react-form/src/useField.tsx b/packages/react-form/src/useField.tsx index 5e28e18..e62ba1a 100644 --- a/packages/react-form/src/useField.tsx +++ b/packages/react-form/src/useField.tsx @@ -3,44 +3,51 @@ import { useStore } from '@tanstack/react-store' import type { DeepKeys, DeepValue, - FieldApiOptions, Narrow, + ResolveData, } from '@tanstack/form-core' -import { FieldApi, type FormApi, functionalUpdate } from '@tanstack/form-core' +import { FieldApi, functionalUpdate } from '@tanstack/form-core' import { useFormContext, formContext } from './formContext' import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect' import type { UseFieldOptions } from './types' declare module '@tanstack/form-core' { // eslint-disable-next-line no-shadow - interface FieldApi<_TData, TFormData, Opts, TData> { - Field: FieldComponent + interface FieldApi< + TData, + TParentData, + TName extends DeepKeys, + TResolvedData extends ResolveData = ResolveData< + TData, + TParentData, + TName + >, + > { + Field: FieldComponent } } -export type UseField = >( - opts?: { name: Narrow } & UseFieldOptions< - DeepValue, - TFormData +export type UseField = >( + opts?: { name: Narrow } & UseFieldOptions< + DeepValue, + TParentData, + TName >, -) => FieldApi, TFormData> +) => FieldApi, TParentData, TName> export function useField< TData, - TFormData, - TName extends unknown extends TFormData - ? string - : DeepKeys = unknown extends TFormData - ? string - : DeepKeys, + TParentData, + TName extends DeepKeys, >( - opts: UseFieldOptions, + opts: UseFieldOptions, ): FieldApi< TData, - TFormData, - Omit & { - form: FormApi - } + TParentData, + TName + // Omit & { + // form: FormApi + // } > { // Get the form API either manually or from context const { formApi, parentFieldName } = useFormContext() @@ -88,17 +95,13 @@ export function useField< } type FieldComponentProps< + TData, TParentData, - TFormData, - TField, - TName extends unknown extends TFormData ? string : DeepKeys, + TName extends DeepKeys, + TResolvedData extends ResolveData, > = { children: ( - fieldApi: FieldApi< - TField, - TFormData, - FieldApiOptions - >, + fieldApi: FieldApi, ) => any } & (TParentData extends any[] ? { @@ -109,24 +112,27 @@ type FieldComponentProps< name: TName index?: never }) & - Omit, 'name' | 'index'> + Omit, 'name' | 'index'> -export type FieldComponent = < - // Type of the field - TField, - // Name of the field - TName extends unknown extends TFormData ? string : DeepKeys, +export type FieldComponent = < + TData, + TName extends DeepKeys, + TResolvedData extends ResolveData = ResolveData< + TData, + TParentData, + TName + >, >({ children, ...fieldOptions -}: FieldComponentProps) => any +}: FieldComponentProps) => any -export function Field({ +export function Field>({ children, ...fieldOptions }: { - children: (fieldApi: FieldApi) => any -} & UseFieldOptions) { + children: (fieldApi: FieldApi) => any +} & 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 ff7aa70..b64c8b7 100644 --- a/packages/react-form/src/useForm.tsx +++ b/packages/react-form/src/useForm.tsx @@ -11,7 +11,7 @@ declare module '@tanstack/form-core' { // eslint-disable-next-line no-shadow interface FormApi { Provider: (props: { children: any }) => any - Field: FieldComponent + Field: FieldComponent useField: UseField useStore: >>( selector?: (state: NoInfer>) => TSelected, diff --git a/packages/vue-form/src/createFormFactory.ts b/packages/vue-form/src/createFormFactory.ts index e5e3547..44113cc 100644 --- a/packages/vue-form/src/createFormFactory.ts +++ b/packages/vue-form/src/createFormFactory.ts @@ -6,7 +6,7 @@ import { useForm } from './useForm' export type FormFactory = { useForm: (opts?: FormOptions) => FormApi useField: UseField - Field: FieldComponent + Field: FieldComponent } export function createFormFactory( diff --git a/packages/vue-form/src/types.ts b/packages/vue-form/src/types.ts index c8eb121..08068a4 100644 --- a/packages/vue-form/src/types.ts +++ b/packages/vue-form/src/types.ts @@ -2,8 +2,8 @@ import type { FieldOptions, DeepKeys } from '@tanstack/form-core' export type UseFieldOptions< TData, - TFormData, - TName = unknown extends TFormData ? string : DeepKeys, -> = FieldOptions & { + TParentData, + TName extends DeepKeys, +> = FieldOptions & { mode?: 'value' | 'array' } diff --git a/packages/vue-form/src/useField.tsx b/packages/vue-form/src/useField.tsx index 1e08e1f..9de1885 100644 --- a/packages/vue-form/src/useField.tsx +++ b/packages/vue-form/src/useField.tsx @@ -1,9 +1,10 @@ -import { - FieldApi, - type FieldApiOptions, - type FormApi, +import { FieldApi } from '@tanstack/form-core' +import type { + DeepKeys, + DeepValue, + Narrow, + ResolveData, } 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-demi' import type { SlotsType, SetupContext, Ref } from 'vue-demi' @@ -12,44 +13,52 @@ import type { UseFieldOptions } from './types' declare module '@tanstack/form-core' { // eslint-disable-next-line no-shadow - interface FieldApi<_TData, TFormData, Opts, TData> { - Field: FieldComponent + interface FieldApi< + TData, + TParentData, + TName extends DeepKeys, + TResolvedData extends ResolveData = ResolveData< + TData, + TParentData, + TName + >, + > { + Field: FieldComponent } } -export type UseField = >( - opts?: { name: Narrow } & UseFieldOptions< - DeepValue, - TFormData +export type UseField = >( + opts?: { name: Narrow } & UseFieldOptions< + DeepValue, + TParentData, + TName >, -) => FieldApi, TFormData> +) => FieldApi, TParentData, TName> export function useField< TData, - TFormData, - TName extends unknown extends TFormData - ? string - : DeepKeys = unknown extends TFormData - ? string - : DeepKeys, + TParentData, + TName extends DeepKeys, >( - opts: UseFieldOptions, + opts: UseFieldOptions, ): { api: FieldApi< TData, - TFormData, - Omit & { - form: FormApi - } + TParentData, + TName + // Omit & { + // form: FormApi + // } > state: Readonly< Ref< FieldApi< TData, - TFormData, - Omit & { - form: FormApi - } + TParentData, + TName + // Omit & { + // form: FormApi + // } >['state'] > > @@ -91,17 +100,16 @@ export function useField< return { api: fieldApi, state: fieldState } as never } -export type FieldValue = TFormData extends any[] - ? unknown extends TField - ? TFormData[number] - : DeepValue - : DeepValue +export type FieldValue = TParentData extends any[] + ? unknown extends TName + ? TParentData[number] + : DeepValue + : DeepValue type FieldComponentProps< + TData, TParentData, - TFormData, - TField, - TName extends unknown extends TFormData ? string : DeepKeys, + TName extends DeepKeys, > = (TParentData extends any[] ? { name?: TName @@ -111,40 +119,35 @@ type FieldComponentProps< name: TName index?: never }) & - Omit, 'name' | 'index'> + Omit, 'name' | 'index'> -export type FieldComponent = < - // Type of the field - TField, - // Name of the field - TName extends unknown extends TFormData ? string : DeepKeys, +export type FieldComponent = < + TData, + TName extends DeepKeys, + TResolvedData extends ResolveData = ResolveData< + TData, + TParentData, + TName + >, >( - fieldOptions: FieldComponentProps, + fieldOptions: FieldComponentProps, context: SetupContext< {}, SlotsType<{ default: { - field: FieldApi< - TField, - TFormData, - FieldApiOptions - > - state: FieldApi< - TField, - TFormData, - FieldApiOptions - >['state'] + field: FieldApi + state: FieldApi['state'] } }> >, ) => any export const Field = defineComponent( - ( - fieldOptions: UseFieldOptions, + >( + fieldOptions: UseFieldOptions, context: SetupContext, ) => { - const fieldApi = useField({ ...fieldOptions, ...context.attrs }) + const fieldApi = useField({ ...fieldOptions, ...context.attrs } as any) provideFormContext({ formApi: fieldApi.api.form, diff --git a/packages/vue-form/src/useForm.tsx b/packages/vue-form/src/useForm.tsx index 8787609..af713fb 100644 --- a/packages/vue-form/src/useForm.tsx +++ b/packages/vue-form/src/useForm.tsx @@ -14,7 +14,7 @@ declare module '@tanstack/form-core' { interface FormApi { Provider: (props: Record & {}) => any provideFormContext: () => void - Field: FieldComponent + Field: FieldComponent useField: UseField useStore: >>( selector?: (state: NoInfer>) => TSelected,