I’ve been trying to come up with a way to dynamically generate components using a configurable object, ideally one that can be store in a .JSON
file. The current solution I’ve come up with works in this exact way but produces an implicit any
type, leading me to believe something isn’t entirely correct.
Types
export type InputFieldTypes = "string" | "number" | "date" | "dropdown" | "object";
export interface BaseComponentProps<TType extends InputFieldTypes, TValue> {
fieldKey: string;
type: TType;
readonly?: boolean;
value: TValue;
onChange: (value: TValue) => void;
}
export type InputField = StringInputComponentProps | NumberInputComponentProps;
export type InputConfig = Omit<InputField, "value" | "onChange">;
export interface StringInputComponentProps extends BaseComponentProps<"string", string> {}
export interface NumberInputComponentProps extends BaseComponentProps<"number", number> {}
Component
export function DynamicInputField(props: InputField) {
switch (props.type) {
case "string":
return <StringField {...props} />;
case "number":
return <NumberField {...props} />;
}
}
Calling works as followed:
<DynamicInputField
{...field}
value={_.get(profile, field.fieldKey)}
onChange={(newValue) => { // <====== TS error occurs here
const newProfile= _.set({ ...profile }, field.fieldKey, newValue);
setProfile(newProfile);
}}
/>
The band aid solution would be to alter the tsconfig
or whack a unknown
type as followed (test: unknown) =>
on the property value. What I’d like though is for TS to automatically understand that the parameter for onChange
should, in the example, take the form of string | number
. I believe its related to the use of an anonymous function being passed which TS doesn’t understand is actually the type we want and the value being pulled from an external object.