Skip to content

Add defineArrayField for type-safe array field management (mirrors defineField pattern) #5112

@zalment

Description

@zalment

Is your feature request related to a problem? Please describe.

When using useForm with TypeScript, defineField provides excellent type safety - it constrains paths to valid form paths and infers the correct value type.

However, for array fields, we must use useFieldArray which:

  1. Uses provide/inject internally, losing the direct connection to the typed form
  2. Accepts any string path, not just paths that resolve to arrays
  3. The array item type must be manually specified: useFieldArray<MyItemType>('items')

This creates an inconsistency:

const { defineField } = useForm<MyForm>();

// ✅ Type-safe: path is constrained, value type is inferred
const [name] = defineField('user.name'); // Ref<string>

// ❌ Not type-safe: any string accepted, item type not inferred
const { fields } = useFieldArray<Item>('items'); // Manual type required
const { fields } = useFieldArray('typo'); // No error for invalid path

Describe the solution you'd like

Add a defineArrayField function to the useForm return object that mirrors defineField:

const { defineField, defineArrayField } = useForm<MyForm>();

// Type-safe array field management:
// - Only accepts paths that resolve to array types (ArrayPath<TValues>)
// - Infers the array item type automatically
const { fields, push, remove } = defineArrayField('items');
//      ^-- FieldEntry<Item>[]     ^-- (item: Item) => void

API:

defineArrayField<TPath extends ArrayPath<TValues>>(
  path: MaybeRefOrGetter<TPath>
): FieldArrayContext<InferredItemType>

Benefits:

  • Path autocomplete only shows array paths
  • TypeScript error if path doesn't resolve to an array
  • Array item type is automatically inferred
  • Consistent with defineField pattern
  • No need for provide/inject when used with useForm

Describe alternatives you've considered

  1. Use useFieldArray in the same component as useForm - Works, but loses type safety. The inject still finds the form, but the path is untyped.

  2. Manually type useFieldArray<ItemType>('path') - Works, but requires manual type annotation and doesn't validate the path.

  3. Create a wrapper function - Users could wrap useFieldArray themselves, but this should be a first-class API since defineField already exists for single fields.


Additional context

This mirrors the existing relationship in the codebase:

Single Field Array Field
inject-based useField useFieldArray
form-direct (typed) defineField defineArrayField ❌ (missing)

The implementation would reuse the existing useFieldArray logic internally, just with:

  1. Form passed directly (no inject)
  2. Path constrained to ArrayPath<TValues>
  3. Item type inferred from PathValue

I have an implementation ready and would be happy to submit a PR if there's interest.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions