How to use useState hook in React with Typescript

I recently stumbled upon this question on Stack Overflow:

I am trying to make a login form in react with typescript. But setEmail method is not accepting value. It says Argument of type 'string' is not assignable to parameter of type 'SetStateAction'. What should I do to solve it?

If you've used React with Typescript before, you've probably seen it yourself as well.

So what's going on? What does the error message mean?

If I were to rewrite that error message and make it more verbose, it would sound something like "the value e.target.value (of type string) cannot be passed as an argument to the setEmail function - since its type is SetStateAction<undefined>, which means it expects an undefined value"

While rewriting the error message clarifies a bit what it refers to, it still makes no sense that it would expect an undefined value to be passed! Why is that?

Why does setEmail expect an undefined value?

It's because when defining the email state, no type was explicitly defined.

Theoretically, when using the useState hook with Typescript, you would need to explicitly specify the type of state you're defining:

const [email, setEmail] = useState<string>();

If you don't do this, Typescript will try to just guess what value you're expecting. That's why it will fill the blanks for you and "translate" useState() to useState<undefined>().

Does this mean you always need to pass in the type when calling useState?

Not necessarily. If you pass in an initial state value, Typescript might be able to correctly guess the type.

I said before that given useState(), Typescript will just assume you meant useState<undefined>().

However, if you passed an empty string as an initial value -  useState('') -, Typescript will be able to figure out you passed a string and will assume you mean useState<string>('').

This is called Typescript inference and it's quite powerful.

Fixing the error

So, to wrap up, in order to fix the error, you have two options:

1. Explicitly type the state when initially declaring it:

const [email, setEmail] = useState<string>();

2. Help Typescript infer the state type by passing in an initial value:

const [email, setEmail] = useState('');

That's it! I hope you found this useful. Let me know in the comments below if you still have any questions :)