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

Ilja picture Ilja · Nov 16, 2018 · Viewed 29.8k times · Source

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;
}

Answer

Nurbol Alpysbayev picture Nurbol Alpysbayev · Nov 16, 2018

Can you try this and tell me if it works?

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>
}