react-reactive-form icon indicating copy to clipboard operation
react-reactive-form copied to clipboard

Incorrect TypeScript typings for render function of FieldArray, et al

Open rjgotten opened this issue 1 year ago • 0 comments

Describe the bug The FieldArray ; FieldControl and FieldGroup components all share the same render function type definition via the GroupProps interface:

render?: (
    control: FormArray | FormControl | FormGroup
  ) => React.ReactElement<any> | React.ReactElement<any>[]

However, FieldArray will actually always use a FormArray; FieldControl will always use a FormControl; and FieldGroup will always use a FormGroup.

The use of a union type on the render function and forcing these three distinct signatures to be squished together means that when accessing any implementation-specific members such as the controls array on a FormArray, explicit type narrowing has to be added.

It's not possible to simply define the control prop in the callback to narrow the type, e..g

<FieldArray render="{( control: FormArray ) => {
  // ...
})}"/>

as this is a TS compiler error due to an incompatible type signature between the provided render function and its declared type on GroupProps. The callback needs to be able to accept anything matching the union type.

So what's left is two options: Either add actual logic in the render function in the vein of if (control instanceof FormArray) { or hard casts in the render function body, which is purely ceremonious overhead and is, basically, noise. Because the underlying type will de facto always be the narrowed one we expect.

Or we apply type erasure with any, e.g.

<FieldArray render="{( control: any ) => {
  // ...
})}"/>

which is pretty damn bad. Because, well: it's type erasure. There goes type safety...

Expected behavior The render function is appropriately typed with the right type of control for each of the three component types. This could for instance be achieved by turning GroupProps into a generic interface that allows passing the expected control type, so that we get e.g.

export class FieldGroup extends React.Component<GroupProps<FormGroup>, any> {}
export class FieldArray extends React.Component<GroupProps<FormArray>, any> {}
export class FieldControl extends React.Component<GroupProps<FormControl>, any> {}

rjgotten avatar Jun 30 '23 09:06 rjgotten