Describe the bug
A field group created with withFieldGroup from createFormHook rejects form={form} when form comes from extendForm on that same hook, even when the parent form values satisfy DeepKeysOfType.
extendForm only adds field/form components. Runtime is the same context and the same form API. The extra components are unused by the group. This should be a valid assignment (width subtyping). TypeScript rejects it because FieldComponent marks ExtendedApi as in out (invariant).
This is separate from value-level variance. Extra keys on the row (status, meeting_room_id, …) already work, as documented. Extra keys on the component map do not.
Your minimal, reproducible example
import { createFormHook, createFormHookContexts } from '@tanstack/react-form'
const { fieldContext, formContext } = createFormHookContexts()
function TextField() {
return null
}
function ExtraField() {
return null
}
const base = createFormHook({
fieldContext,
formContext,
fieldComponents: { TextField },
formComponents: {},
})
const extended = base.extendForm({
fieldComponents: { ExtraField },
})
const Group = base.withFieldGroup({
defaultValues: { name: '' },
render: function Render({ group }) {
return (
<group.AppField name="name">{(field) => <field.TextField />}</group.AppField>
)
},
})
function App() {
const form = extended.useAppForm({
defaultValues: { name: '' },
})
// Type error on `form`.
// A form from `base.useAppForm` with the same defaultValues typechecks.
return <Group form={form} fields={{ name: 'name' }} />
}
Steps to reproduce
- Create a shared hook with
createFormHook.
- Create
withFieldGroup from that hook.
- Call
extendForm and add one extra field or form component.
- Pass the extended
form into the shared group.
Expected behavior
The extended form should be assignable to the parent hook's withFieldGroup.
extendForm spreads the parent component maps and reuses fieldContext / formContext:
https://github.com/TanStack/form/blob/main/packages/react-form/src/createFormHook.tsx
An extended form is the base form plus extra component keys. The group only uses the base keys (TextField in the repro). Extra components sit unused.
ExtendedApi only appears on the object passed into AppField's children:
children: (fieldApi: FieldApi<…> & ExtendedApi) => ReactNode
That is an output. The safe variance is out ExtendedApi:
AppField<Base & { ExtraField }> as AppField<Base>: the group only uses TextField. Sound.
AppField<Base> as AppField<Base & { ExtraField }>: the callback would read ExtraField and get undefined. Correctly rejected.
Today FieldComponent pins every slot, including ExtendedApi, as in out:
https://github.com/TanStack/form/blob/main/packages/react-form/src/useField.tsx
withFieldGroup then requires the exact TComponents / TFormComponents from the hook that created the group, so the extra keys fail.
TParentData staying invariant is right. The group reads and writes values. That is issue 1855, a different problem.
What a fix in TanStack looks like
The small change: in out ExtendedApi becomes out ExtendedApi on FieldComponent and the field-group equivalent.
The form prop can also be "at least these components":
form: AppFieldExtendedReactFormApi<
TFormData,
TOnMount,
/* ... */,
TComponents & Record<string, ComponentType<any>>,
TFormComponents & Record<string, ComponentType<any>>
>
That only works if ExtendedApi is covariant. While it stays in out, no amount of & Extra on the call site will assign.
How often does this bug happen?
Every time
Platform
TypeScript 5.x / 6.x, React
TanStack Form adapter
react-form
TanStack Form version
1.33.2
TypeScript version
5.x / 6.x
Additional context
Related, not a duplicate: 1855 (value map too wide). That issue is about writing a wider field value into a narrower parent field. This issue is only the registered component map.
Describe the bug
A field group created with
withFieldGroupfromcreateFormHookrejectsform={form}whenformcomes fromextendFormon that same hook, even when the parent form values satisfyDeepKeysOfType.extendFormonly adds field/form components. Runtime is the same context and the same form API. The extra components are unused by the group. This should be a valid assignment (width subtyping). TypeScript rejects it becauseFieldComponentmarksExtendedApiasin out(invariant).This is separate from value-level variance. Extra keys on the row (
status,meeting_room_id, …) already work, as documented. Extra keys on the component map do not.Your minimal, reproducible example
Steps to reproduce
createFormHook.withFieldGroupfrom that hook.extendFormand add one extra field or form component.forminto the shared group.Expected behavior
The extended form should be assignable to the parent hook's
withFieldGroup.extendFormspreads the parent component maps and reusesfieldContext/formContext:https://github.com/TanStack/form/blob/main/packages/react-form/src/createFormHook.tsx
An extended form is the base form plus extra component keys. The group only uses the base keys (
TextFieldin the repro). Extra components sit unused.ExtendedApionly appears on the object passed intoAppField'schildren:That is an output. The safe variance is
out ExtendedApi:AppField<Base & { ExtraField }>asAppField<Base>: the group only usesTextField. Sound.AppField<Base>asAppField<Base & { ExtraField }>: the callback would readExtraFieldand getundefined. Correctly rejected.Today
FieldComponentpins every slot, includingExtendedApi, asin out:https://github.com/TanStack/form/blob/main/packages/react-form/src/useField.tsx
withFieldGroupthen requires the exactTComponents/TFormComponentsfrom the hook that created the group, so the extra keys fail.TParentDatastaying invariant is right. The group reads and writes values. That is issue 1855, a different problem.What a fix in TanStack looks like
The small change:
in out ExtendedApibecomesout ExtendedApionFieldComponentand the field-group equivalent.The
formprop can also be "at least these components":That only works if
ExtendedApiis covariant. While it staysin out, no amount of& Extraon the call site will assign.How often does this bug happen?
Every time
Platform
TypeScript 5.x / 6.x, React
TanStack Form adapter
react-form
TanStack Form version
1.33.2
TypeScript version
5.x / 6.x
Additional context
Related, not a duplicate: 1855 (value map too wide). That issue is about writing a wider field value into a narrower parent field. This issue is only the registered component map.