I developed a react hook that resembles the following structure:
export const useForm = <T>(values: T) => {
const [formData, setFormData] = useState<FormFieldData<T>>({});
useEffect(() => {
const fields = {};
for (const key in values) {
const value = values[key];
fields[key] = {
value,
errorMsg: '',
};
}
setFormData(fields);
}, []);
return {
formData,
};
};
This hook accepts an object with various fields and their default values, for example:
const { formData } = useForm({
stringField: '',
numberField: 1,
booleanField: false,
});
The hook then uses these keys and default values to construct and return an object called formData
:
{
stringField: {
value: '',
errorMsg: '',
},
numberField: {
value: 1,
errorMsg: '',
},
booleanField: {
value: false,
errorMsg: '',
},
}
To correctly type the formData
, I defined a type named FormFieldData
. This type takes in the generic type parameter passed to the hook and generates a type for formData
based on the provided keys and values:
export type FormField<T> = {
errorMsg: string;
value: T;
};
export type FormFieldData<T> = {
[K in keyof T]: FormField<T[keyof T]>;
};
The issue arises when each value in the field object within formData
turns into a union type of all the different types included in the original hook call. For instance, if we take the earlier example, formData.stringField.value
would have a type of string | number | boolean
.
What I expect is for the type of each field's value
property to match the exact type of the original value assigned to that field in the initial hook call. Therefore, formData.stringField.value
should be of type string
, and formData.numberField.value
should be of type number
.
Am I overlooking something here?
How does T[keyof T]
result in a union of all types present in the generic object?
Update
I resolved this issue by modifying the FormFieldData type to:
export type FormFieldData<T> = {
[K in keyof T]: FormField<T[K]>;
};