When working with a Typescript React form builder, I encountered a situation where each component had different types for the value and onChange properties. To tackle this issue, I decided to utilize generics so that I could define the expected types for each component.
Here is a simplified interface for the lowest level form question component:
interface FormFieldProps<T> {
value: T;
onChange: (value: { [key: string]: T }) => void;
}
And here is a simplified interface for the top-level FormBuilder component:
interface FormBuilderProps<T> {
fields: FormFieldProps<T>[];
onChange: (value: { [key: string]: T }) => void;
formData: { [key: string]: T };
}
The challenge I faced was that I had to provide a generic type at the highest level in the FormBuilder, but only 'unknown' or 'any' seemed to be suitable options. This posed a problem as some components required 'string', 'string[]', 'number', etc.
To illustrate, here is a sample component definition:
const FormBuilder = (props: FormBuilderProps<unknown>) => {...}
Although I managed to eliminate type errors by passing down 'unknown' as the generic from the top-level FormBuilder component, all typings became 'unknown'. This led to situations where I could inadvertently pass a 'number' to a component expecting a 'string', for example.
Updated Statement:
An issue arises when attempting to set typed data for FormBuilderProps. The necessity of passing the generic type from top to bottom results in mismatches like 'boolean is not assignable to type string' for certain components. Should I manually handle each potential option for T and implement type guards in every component?
Each item within the 'fields' array is processed through a switch statement based on the componentType, leading to the rendering of the appropriate component. Utilizing these enums to refine the type of T would be advantageous.
export const formBuilderData: FormBuilderProps<string> = {
fields: [
{
index: 1,
columnName: 'columnName',
componentType: PureComponentType.TEXT,
value: 'alphabet',
},
{
index: 2,
columnName: 'bestBear',
componentType: PureComponentType.SWITCH,
value: true,
],
readOnly: false,
formData: {},
};