Skip to content

fix(form-core): [v2] set array values at the given index past the end - #2479

Open
KooYS wants to merge 1 commit into
TanStack:alphafrom
KooYS:fix/setby-past-end-index
Open

KooYS wants to merge 1 commit into
TanStack:alphafrom
KooYS:fix/setby-past-end-index

Conversation

@KooYS

@KooYS KooYS commented Nov 13, 2026 •

Copy link
Copy Markdown

🎯 Changes

Writing past the end of a non-empty array puts the value at length instead of at the index. With defaultValues: { phones: ['555-0000'] }, typing into phones[3] stores the value at phones[1], where a later phones[1] input overwrites it. resetField('phones[3]') on a shortened array has the same problem.

setBy only pads the gap when the slice before the index is empty (utils.lib.ts#L184), so setBy([], '[2]', 'third') already gives [undefined, undefined, 'third']. This pads it in both cases:

-        ...(prefix.length ? prefix : new Array(key)),
+        ...prefix,
+        ...new Array(key - prefix.length),

One existing assertion changes. sets array values and preserves sparse indexes (added in 7de9faa) expects setBy(['first'], '[2]', 'third') to equal ['first', 'third']. It now expects ['first', undefined, 'third'], matching the test name and the empty-array assertion below it. If the old result is intended, could you point me to the case it covers? If you'd rather reject past-end writes like the array methods do (warn and no-op), I can switch to that.

Tests: typing into phones[3] then phones[1] keeps both values; resetField('phones[3]') after shortening restores the default at index 3. Both, and the changed assertion, fail on alpha and pass with the fix.

Behavior change: the array grows to index + 1 with undefined in the gap, so field.value.map(...) and schemas like z.array(z.string()) will see those slots. A numeric key on a missing parent still creates an object (#993).

The same code is on main (v1); happy to open a backport.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested code changes locally with pnpm test:pr, or these tests do not apply to this pull request.
  • I fully understand the code in this pull request, including any code generated with AI assistance.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes
    • Fixed setting form values at indexes beyond an array’s end: existing values are preserved, intermediate indexes remain empty, and those fields can be updated or reset independently.

setBy only padded the array when the slice before the index was empty,
so writing past the end of a non-empty array placed the value at
`length` instead of at the index. Pad the gap with undefined, which is
what already happens for empty arrays.
@changeset-bot

changeset-bot Bot commented Nov 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 429d060

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@tanstack/form-core Patch
@tanstack/angular-form Patch
@tanstack/form-devtools Patch
@tanstack/lit-form Patch
@tanstack/preact-form Patch
@tanstack/react-form-nextjs Patch
@tanstack/react-form-start Patch
@tanstack/react-form Patch
@tanstack/solid-form Patch
@tanstack/svelte-form Patch
@tanstack/vue-form Patch
@tanstack/react-form-devtools Patch
@tanstack/solid-form-devtools Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Explain Change →

Walkthrough

setBy now fills array gaps based on the existing prefix length when setting an indexed value. Tests cover updates and resets beyond the end of an array, and a Changeset records a patch release for @tanstack/form-core.

Changes: Array index updates

Layer / File(s) Summary
Array gap filling and field behavior
packages/form-core/src/utils.lib.ts, packages/form-core/tests/FormApi/FormApi.lib.spec.ts, packages/form-core/tests/utils.test.ts, .changeset/quiet-slots-stay.md
setBy preserves the existing prefix and fills intermediate indexes with undefined. Tests cover setting and resetting indexes beyond the array’s end. A Changeset records the patch release.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: lecarbonator

Merge Risk: Low · up to 429d0

Writing to an array index past the end now fills the gap with undefined and puts the value at the requested index. This fixes the previous misplacement. Code that assumes every element of a string[] field is defined, such as value.map(p => p.trim()), can now hit undefined entries. The PR states this behavior is intended, so owners should be aware of it, and it is not clearly a blocker.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title names the main change: array writes past the end of a non-empty array now pad the gap so the value lands at the requested index. It also names the affected package (form-core) and the fix ty…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description follows the repository template. It explains the bug: writing past the end of a non-empty array stores the value at length instead of the requested index. It names the fix in setBy…
Full details: Title check

Explanation

The title names the main change: array writes past the end of a non-empty array now pad the gap so the value lands at the requested index. It also names the affected package (form-core) and the fix type. It matches the changes in utils.lib.ts, the tests, and the changeset.

Full details: Description check

Explanation

The description follows the repository template. It explains the bug: writing past the end of a non-empty array stores the value at length instead of the requested index. It names the fix in setBy in utils.lib.ts, and it lists the test coverage. It also flags the one changed existing assertion and the behavior change for consumers such as field.value.map(...) and z.array(z.string()). The Changes section is detailed. The checklist is filled in, and the release impact box is ticked with a changeset generated, which matches the raw summary's changeset file. The description does not explicitly name the test command result beyond the checkbox, but the checklist covers it.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional comments: 1

packages/form-core/src/utils.lib.ts-185-185: 1

185-185: Major · Data Integrity & Integration · Heavy lift

Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.

Preserve the form value's element-type contract.

For the new phones: string[] test, writing phones[3] puts undefined in form.state.values.phones, which remains typed as string[]. A consumer such as phones.map(phone => phone.trim()) then throws. Prevent the invalid values or expose them through the form's type and API contract.

Review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 27ddd4a and 429d060.

Files selected for processing: 4

  • .changeset/quiet-slots-stay.md
  • packages/form-core/src/utils.lib.ts
  • packages/form-core/tests/FormApi/FormApi.lib.spec.ts
  • packages/form-core/tests/utils.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant