0

I'm moving from javascript to typescript, and this is an error that I encounter multiple times:

In a component, I'm awaiting a fetch to return an object, and until that time, the component returns null. If the object is there, I will render some properties of the object:

const Component = () => {
  const [user, setUser] = useState(null)

  useEffect(() => {
    getUser().then(setUser)
  }, [getUser])

  if (!user) return null

  return (
    <ul>
      <li>{`Username: ${user.userName}`}</li>
      <li>{`Email: ${user.email}`}</li>
    </ul>
  )
}

Typescript then throws an error Object is possibly 'null'. TS2531

For a more general case, this question already has an answer here: How to suppress "error TS2533: Object is possibly 'null' or 'undefined'"?

However, assuring that the object is never null doesn't feel right. I mean, I could initialise the object;

const [user, setUser] = useState({ userName: 'UNKNOWN', email: 'UNKNOWN' })

but that clearly doesn't seem to be the best solution to me either.

I'm disappointed that typescript doesn't recognise that the user can never be null when it reaches the JSX. But this is probably because I'm thinking way to naively about this.

Remains the question - how do I deal with this typical scenario in a typescript component?

Sventies
  • 2,314
  • 1
  • 28
  • 44

1 Answers1

1

Issue here is that you don't tell to TypeScript, what type can have the user state. To fix it, just define a type for User and specify in the useState:

type User = {
  userName: string;
  email: string;
};

const [user, setUser] = React.useState<User | null>(null);

Edit friendly-mountain-g9m5x

johannchopin
  • 13,720
  • 10
  • 55
  • 101
  • Thanks, works like a charm. One more question @johannchopin - is this (assigning null to the initial state) the recommended way of going about this? – Sventies Sep 25 '20 at 10:50
  • @Sventies Yes you should always define a type for your state like that `React.useState(value);` – johannchopin Sep 25 '20 at 10:51
  • I mean if assigning null to the initial state in the first place is the recommended approach – Sventies Sep 25 '20 at 10:53
  • @Sventies Yes I guess it's a good way to handle your asynchronous state. Another solution would be to use `undefined` instead of `null` but the meaning is not the same. `undefined` means that it doesn't exist. `null` tell that it exist, but doesn't have a consumable value. So in this case its perfect to use `null` because it clearly indicates that the `user` state exist, but that the consumable value isn't set yet. So yeah it's a good choice to use `null`. – johannchopin Sep 25 '20 at 10:57
  • 1
    @Sventies and in the template, you can use a safe navigation operator like this: `user?.userName` this approach will render an empty list for you with empty list elements. In some cases, this is also can be a solution. – Anarno Sep 25 '20 at 11:06