Repository navigation
[RFC] form-core: Handling Array Methods Called on null or undefined #1823
Description
Activity
- changed the title
[-]-[/-][+][RFC] form-core: Handling Array Methods Called on `null` or `undefined`[/+]on Oct 24, 2025 - pinned this issue
on Oct 24, 2025 We use GO for our backend and usually use backend response as our default values for forms.
Since GO slices are pointers with a zero value of nil, we treat all our arrays as nullable() for safety.It is also quite common in UI library to have null represent the default value for Select or Combobox, some of which have a "multiple" mode allowing to pick multiple choices.
For users of such components, I assume it would be convenient for an array field to start as null or undefined.
We believe Option 2. Create an empty array upon using an array method would be the most convenient behaviour for these scenarios. 😊
Reacted by Juan Villa and Ilya MolostovHappened upon this issue so I figured I'd give my two cents.
I tend to prefer explicit typing and so I don't really see the benefit of allowing null/undefined and automatically creating an empty array. Checking if an array has zero length is about the same amount of code in javascript as checking if something is null.
I like to think of the form's type as it's interface. It shouldn't matter how it will be saved later, the form has a certain shape and the story is told in that shape. For me, an array that might not have anything in it should still be an array.
As for other language interfaces like Go for example I'm not sure what the benefit would be there but I don't know enough about that use case to refute any potential benefits as my day to day is in javascript.
My vote would be option 1, throw an error if null or undefined is found. Avoiding hidden coercion is more valuable to me than convenience generally and I don't see how the coercion provides much convenience to begin with.
Reacted by Igor Adrov, 2yunseong, Tim Hazlett and JoshI’d vote for Option 1 (Throw an Error) as well.
To me,
nullorundefineddoesn’t mean quite the same thing as[]. A nullable value might mean the field hasn’t been initialized or doesn’t apply, while an empty array means it’s initialized but has no items.Throwing an error makes unexpected form values obvious and avoids hiding problems with the initial values or the API mapping.
The array helpers shouldn’t have to make that decision on the consumer’s behalf.
- addedv1This issue has been created during v1 development.This issue has been created during v1 development.v2This issue affects v2 as well.This issue affects v2 as well.scope: coreThis issue affects the core package, meaning any adapter is also affected by it.This issue affects the core package, meaning any adapter is also affected by it.
on Aug 9, 2026 Our vote goes to option 2 ("Create an empty array") — with one caveat regarding empty results (see the "Arrays" section).
Context
We generate code from OpenAPI using hey-api and zod-empty (a customized fork).
Strings
For the OpenAPI schema
Foo: type: object properties: bar: type: string
hey-apigenerates the Zod schemaconst schema = z.object({ bar: z.string().optional(), });
and
zod-emptyderives the form's initial state from the Zod schema:const defaultValues = { bar: undefined, };
The schema above corresponds to the type
type DefaultValues = { bar?: string; };
Here it is clearly visible that the field is optional. To use an empty string as the initial state for the required
bar, we would have to switch to the OpenApi schemeFoo: type: object properties: bar: type: string minLength: 1
Instead of
Foo: type: object properties: bar: type: string required: [bar]
This looks less idiomatic and, worst of all, is not reflected in the static type.
Having two semantically equivalent states —
''andundefined— is a bad idea. That is why it is better if''is disallowed and the model transitions to theundefinedstate instead. This differs from the TanStack Form docs examples, where an empty string is often used for string fields, but this approach is more idiomatic from a TypeScript and OpenAPI perspective. That said, the React warningA component is changing an uncontrolled input to be controlled...
can be avoided as follows:
<input value={field.state.value ?? ''} onChange={(e) => field.handleChange(e.target.value ? e.target.value : undefined)} />
Usually we build forms to send data over the network, and here
undefinedalso has advantages:JSON.stringify({ bar: undefined }) // -> {} JSON.stringify({ bar: '' }) // -> {"bar":""}
Arrays
The same logic applies to arrays, which is why I consider "Create an empty array" the best option.
Additionally, I propose that any operation resulting in an empty array should produce
undefinedinstead of[]. In particular,removeFieldValuewould set the value toundefinedrather than[]when the last element is removed.Such behavior aligns better with TypeScript and OpenAPI. It would let us leverage code generation more broadly and eliminate a lot of boilerplate.
RFC (form-core): Handling Array Methods Called on
nullorundefinedBackground
TanStack Form provides several helper methods for working with array fields.
In version
[email protected], these include:moveFieldValuespushFieldValueswapFieldValuesclearFieldValuesinsertFieldValueremoveFieldValuereplaceFieldValueCurrently, these methods can only be used if the field type explicitly extends
unknown[]. However, there have been requests (#1588 ) to support optional arrays.The Problem
TypeScript allows us to express such optional arrays easily, but at runtime the behavior is inconsistent.
Consider this example:
In the current version:
There are no runtime checks ensuring consistent behavior when array methods are called on
nullorundefinedGoal
We want consistent, predictable behavior across all array helper methods when the field value is
nullorundefined.This RFC plans to define that behaviour and make it guarantee the runtime behaviour.
Once a decision is made, all array methods (
swapFieldValues,removeFieldValueetc.) will follow this rule and be tested for it.Your Feedback
If you currently use arrays in your forms, please share your expectations for how the following code should behave:
Here are some of our proposed options. If you have other suggestions or proposals, feel free to share!
nullorundefined.pushFieldValue('people', 'New Person')transformsnull -> ['New Person']swapFieldValues('people', 1, 2)transformsnull -> []Frequently Asked Questions
Will this be a breaking change?
Possibly. The current type definitions don't allow you to call these methods on optional arrays. If a field called the array method, it was an inconsistent mix of doing nothing, throwing an error or creating an array.
With the plans to provide optional array support, we want to enforce consistent behaviour.