Related to #93.
Summary
Formisch currently assumes that a given field path has one structural kind during initialization. Arrays and plain objects are treated specially because they create nested field stores, while all other values are treated as leaf fields backed by a single signal for the value.
That means the same object key or array item cannot currently be:
- a leaf field in one branch, and
- an object or array field in another branch
This is not limited to variant schemas. The same limitation also applies to unions and other schema compositions where multiple branches can reuse the same path with different structural kinds.
Current situation
Today, Formisch builds one shared field-store tree from the schema. This works well as long as each path resolves to a consistent kind across all relevant branches.
The limitation appears when branches reuse the same path but assign incompatible kinds, for example:
value: string in one branch
value: string[] in another branch
item: number in one branch
item: { nested: string } in another branch
In those cases, the same path would need to behave both like a leaf and like a structural node, which the current store model does not support.
For now, the recommended workaround is to use different object keys for fields that have different structural kinds across branches.
Why this exists
This is mostly a tradeoff in the original implementation. Treating arrays and plain objects as structural nodes keeps the store model simple and efficient, while every non-array and non-plain-object value can stay a leaf signal for the value.
Supporting multiple kinds for the same path is theoretically possible, but it introduces significantly more complexity in initialization, reconciliation, updates, and type handling. That complexity was not worth investigating in the initial implementation.
Possible directions
Some possible approaches:
- Make initialization branch-aware and only build the active branch when a schema composition can be narrowed.
- Keep separate branch-local stores and switch between them based on the active schema branch.
- Delay materializing a field store until the active branch is known.
- Add a reconciliation layer that can replace a leaf field with an object or array store, and vice versa, when the active branch changes.
- Introduce a more abstract store representation that can model unresolved paths before collapsing them into a concrete kind.
Each option has tradeoffs around performance, state preservation, implementation complexity, and framework integrations.
Scope
This is a known limitation, and it is probably something we should work on after the v1 release rather than before. The current workaround is acceptable for now, but the limitation should be tracked explicitly because it affects schema design for variants, unions, and similar multi-branch schemas.
Related to #93.
Summary
Formisch currently assumes that a given field path has one structural kind during initialization. Arrays and plain objects are treated specially because they create nested field stores, while all other values are treated as leaf fields backed by a single signal for the value.
That means the same object key or array item cannot currently be:
This is not limited to variant schemas. The same limitation also applies to unions and other schema compositions where multiple branches can reuse the same path with different structural kinds.
Current situation
Today, Formisch builds one shared field-store tree from the schema. This works well as long as each path resolves to a consistent kind across all relevant branches.
The limitation appears when branches reuse the same path but assign incompatible kinds, for example:
value: stringin one branchvalue: string[]in another branchitem: numberin one branchitem: { nested: string }in another branchIn those cases, the same path would need to behave both like a leaf and like a structural node, which the current store model does not support.
For now, the recommended workaround is to use different object keys for fields that have different structural kinds across branches.
Why this exists
This is mostly a tradeoff in the original implementation. Treating arrays and plain objects as structural nodes keeps the store model simple and efficient, while every non-array and non-plain-object value can stay a leaf signal for the value.
Supporting multiple kinds for the same path is theoretically possible, but it introduces significantly more complexity in initialization, reconciliation, updates, and type handling. That complexity was not worth investigating in the initial implementation.
Possible directions
Some possible approaches:
Each option has tradeoffs around performance, state preservation, implementation complexity, and framework integrations.
Scope
This is a known limitation, and it is probably something we should work on after the v1 release rather than before. The current workaround is acceptable for now, but the limitation should be tracked explicitly because it affects schema design for variants, unions, and similar multi-branch schemas.