Reputation: 35
I'm getting the following error for the code below, which I suspect might not be the expected behavior. Basically TS is complaining that rules[name]
might not be callable, which is true because it can be undefined
, but if I guard that case it still throws the same error. I think problem becomes clear by reading the comments in the code.
This expression is not callable.
Not all constituents of type '((value: string | number) => boolean) | undefined' are callable.
Type 'undefined' has no call signatures.ts(2349)
type FieldValues = Record<string, string | number>;
type FieldName<FormValues> = keyof FormValues;
type FormState<FormValues> = {
values: FormValues,
errors: Record<FieldName<FormValues>, boolean>,
focused: Record<FieldName<FormValues>, boolean>,
touched: Record<FieldName<FormValues>, boolean>,
dirty: Record<FieldName<FormValues>, boolean>,
isValid: boolean,
isDirty: boolean,
};
export function useForm<FormValues extends FieldValues> (
defaultValues: FormValues,
rules?: Partial<Record<FieldName<FormValues>, (value: string | number) => boolean>>,
) {
...
const validate = (name: FieldName<FormValues>, value: string | number) => {
// either "if (rules && rules[names])" or "if (rules && rules[name] instanceof Function)"
// should narrow out undefined thus guaranteeing the correct call signature
if (rules && rules[name] instanceof Function) {
setFormState((prevFormState: FormState<FormValues>) => ({
...prevFormState,
errors: {
...prevFormState.errors,
[name]: rules[name](value),
},
isValid: isFormValid({
...prevFormState.errors,
[name]: rules[name](value),
}),
}))
}
}
...
}
I found what I think is a similar issue which has been reported as a bug:
But I cannot make sense of it: i) does this fall under the same type of issue and ii) what is the workaround for this?
Edit: changed the title from 'Guarding for function in union type not working' to 'Guarding for undefined in union type not working' as it describes the problem more accurately.
Upvotes: 1
Views: 313
Reputation: 13297
Unless I'm not seeing something here, wouldn't a simple condition like rules && rules[name]
work? Typescript compiler already knows it's either function or undefined, so if you check that it's truthy, it should be enough.
Upvotes: 1
Reputation: 35
For anyone facing a similar problem a good explanation for why TS has problems with these and a solution for it is provided here.
So what I ended up doing was:
if (rules) {
const fieldRule = rules[name]
if (fieldRule) {
setFormState((prevFormState: FormState<FormValues>) => ({
...prevFormState,
errors: {
...prevFormState.errors,
[name]: fieldRule(value),
},
isValid: isFormValid({
...prevFormState.errors,
[name]: fieldRule(value),
}),
}))
}
}
Upvotes: 0