Reputation: 649
In my React TS component I have a bunch of fields, contrived example below, that check a specific condition, and if it is not met, set the specific field error to true, to be reflected and the component DOM (and thus not submit) However when I have the code below it throws an expression not callable
on the setErr
function.
const App = () => {
const [name, setName] = React.useState("");
const [email, setEmail] = React.useState("");
const [nameError, setNameError] = React.useState(false);
const [emailError, setEmailError] = React.useState(false);
return (
<div className="App">
<input
type="text"
value={name}
style={{
border: `1 px solid ${nameError ? "red" : "black"}`
}}
onChange={e => {
setName(e.target.value);
}}
/>
<input
type="text"
value={email}
onChange={e => {
setEmail(e.target.value);
}}
style={{
border: `1 px solid ${emailError ? "red" : "black"}`
}}
/>
<button
onClick={() => {
const errors = [
[setNameError, name.length],
[setEmailError, email.length]
];
let canSubmit = true;
errors.forEach(validation => {
const [setErr, condition] = validation;
console.log(!condition);
if (!condition) {
canSubmit = false;
setErr(true); // <---- ERROR HERE
}
});
if (canSubmit) { /* submit the form */ }
}}
>
submit
</button>
</div>
);
};
This only errors in TypeScript as it works fine in vanilla/jsx. And doesn't compile in some build systems.
Full error is:
This expression is not callable.
Not all constituents of type 'string | number | boolean | Dispatch<SetStateAction<boolean>>' are callable.
Type 'string' has no call signatures.
I am especially confused why it thinks setErr
is of type string, when it should be equal to the setNameError function destructured from useState.
Upvotes: 4
Views: 6262
Reputation: 138267
All you need is to add as const
to the errors
declaration:
const errors = [
[setNameError, name.length],
[setEmailError, email.length]
] as const;
That way, the arrays won't be typed as arrays but as tuples.
Upvotes: 9
Reputation: 2072
The inferred type of errors
is what's shooting you down here. By your error message we can derive that const errors: (string | number | boolean | Dispatch<SetStateAction<boolean>>)[][]
, hence typescript infers that the array elements can be a bunch of things, some of which are not callable. Instead you could objectify it, and the types inferred will be assigned to keys, allowing you do deconstruct and call properly, i.e.
<button
onClick={() => {
const errors = [
{setError:setNameError, condition:name.length},
{setError:setEmailError, condition:email.length}
];
let canSubmit = true;
errors.forEach(validation => {
const {setError, condition} = validation;
console.log(!condition);
if (!condition) {
canSubmit = false;
setError(true); // <---- ERROR HERE
}
});
if (canSubmit) { /* submit the form */ }
}}
>
Upvotes: 0