Correct way to type nullable state when using React's useState hook

65,938

Solution 1

Currently, the TypeScript compiler thinks the type of email and password are null (and no other value). You can resolve this by providing an explicit type parameter to the useState call so that the types of email and password are known to be string or null.

const { useState } = React;
function Example() {
  const [state, setState] = useState<{email: null | string, password: null | string}>({ email: null, password: null });
  function setEmail(email: string) {
    setState(prevState => ({ ...prevState, email }))
  }
  return <p>{state.email}</p>
}

Solution 2

This is addressed in a few spots already:

https://dev.to/busypeoples/notes-on-typescript-react-hooks-28j2

https://codewithstyle.info/Using-React-useState-hook-with-TypeScript/

TLDR: pass a type argument to setState when you have an empty initial state

eg:

const [email, setEmail] = useState<string>();
Share:
65,938
Ilja
Author by

Ilja

That dev at the local ☕️ shop sipping on late.

Updated on July 09, 2022

Comments

  • Ilja
    Ilja 5 months

    I am having trouble figuring out how to type useState function since it returns a tuple. In essence, I have to provide null as initial value for email i.e. lets assume I can't use empty string here.

    I then have setEmail function to update this state value, which takes in email as string.

    ideally I would like to type my useState so it expects email to be either string or null if possible. At the moment it inherits it as only null

    import * as React from "react";
    const { useState } = React;
    function Example() {
      const [state, setState] = useState({ email: null, password: null });
      function setEmail(email: string) {
        setState(prevState => ({ ...prevState, email }))
      }
      return <p>{state.email}</p>
    }
    

    Following error is returned for setEmail function since string in function argument is not valid type for null specified in useState()

    [ts]
    Argument of type '(prevState: { email: null; password: null; }) => { email: string; password: null; }' is not assignable to parameter of type 'SetStateAction<{ email: null; password: null; }>'.
      Type '(prevState: { email: null; password: null; }) => { email: string; password: null; }' is not assignable to type '(prevState: { email: null; password: null; }) => { email: null; password: null; }'.
        Type '{ email: string; password: null; }' is not assignable to type '{ email: null; password: null; }'.
          Types of property 'email' are incompatible.
            Type 'string' is not assignable to type 'null'. [2345]
    (parameter) prevState: {
        email: null;
        password: null;
    }
    
  • SgtPooki
    SgtPooki over 3 years
    I think you should comment/edit the accepted answer as your answer is not as readable and provides no additional benefits to the existing answer.
  • Admin
    Admin over 3 years
    I think this solution is better considering it use less caracters : repeat null | xxx four times doesn't improve readability ?
  • Nurbol Alpysbayev
    Nurbol Alpysbayev almost 3 years
    Having null properties and not having them at all is not the same.
  • tjeisenschenk about 2 years
    Using a Partial is actually a good idea and helped me :)
  • refaelio
    refaelio almost 2 years
    what about objects?
  • wongz
    wongz about 1 year
    @refaelio Even for objects, if you keep the brackets empty it won't cause issue if you have a defined interface or type. For example, this works const [user, setUser] = useState<UserType>(); whereas this does not work const [user, setUser] = useState<UserType>(null);