JmJ
JmJ

Reputation: 2098

TS2322 - false | Element is not assignable to type ReactElement. Error does not consistently appear in app?

I understand what the error is saying and I have been able to fix it, but I was hoping someone could provide some clarification on why it's happening now and why it isn't happening in other areas of my app, as well as why a function and a ternary with essentially the same type signature produce different results.

I wanted to display a basic error message in the following style:

{!!error.message && (
  <Text>{error.message}</Text>
)}

But it gives the error mentioned in the title. I know false is handled by React and won't render anything, but knowing null is the preferred way of telling React not to render something I converted the above into a component. This makes the error go away:

const Error = () => {
  if (!error.message) return null;
  return <Text>{error.message}</Text>;
}

Out of curiosity, I tried the same thing with a ternary which should have the same signature:

{error.message ? <Text>{error.message}</Text> : null}

However this produces a very similar error to the one in the title, except it complain about null instead.

Knowing all 3 bits of code are valid React, and 2/3 being virtually identical, why is only one accepted by the TS compiler?

To further confuse things, I have the below in another part of my app and TS has no issue with it:

{!loaded && (
  <RootStack.Screen name={SCREENS.SPLASH} component={Splash} />
)}
{loaded && !userAuth.authenticated && (
  <>
    <RootStack.Screen name={SCREENS.SIGN_UP} component={SignUp} />
    <RootStack.Screen name={SCREENS.SIGN_IN} component={SignIn} />
  </>
)}
{loaded && userAuth.authenticated && (
  <RootStack.Screen name={SCREENS.TABS} component={Tabs} />
)}

Upvotes: 15

Views: 15383

Answers (3)

Charana Amarasekara
Charana Amarasekara

Reputation: 76

I know I'm too late , but if you define your return type as JSX.Element | null this problem wont occur.

Upvotes: 0

ThePuzzleMaster
ThePuzzleMaster

Reputation: 991

I ran into the same problem and found another option which worked for me in my case.

I had defined the parent component to what would have been the <Error/> with the following prop.

children: React.ReactElement<ErrorProps>

And so when the parent was receiving false as its children, it spit out the same error.

In order to get around that I changed my prop type to look like this:

children: false | React.ReactElement<ErrorProps>

My component was actually a bit more complex, so it took a little bit of inner conditional logic to handle a false value, but otherwise, it worked great, and I didn't have to sprinkle react fragments all over my code.

Upvotes: 0

Harley Lang
Harley Lang

Reputation: 2353

I also received this error using code similar to yours and similar to your observations, them being that the error can be avoided with React Fragments and occurs when you use conditional logic with a JSX.Element.

I dug into the React documentation regarding the conditional logic:

Inline If with Logical && Operator

You may embed expressions in JSX by wrapping them in curly braces. This includes the JavaScript logical && operator. It can be handy for conditionally including an element...

It works because in JavaScript, true && expression always evaluates to expression, and false && expression always evaluates to false.

Therefore, if the condition is true, the element right after && will appear in the output. If it is false, React will ignore and skip it.

Which means that the following code is attempting to evaluate the <text> element with type JSX.Element as a boolean, so type error.

{!!error.message && (
  <Text>{error.message}</Text>
)}

In terms of a solution to proceed with Inline && Operators conditionals, the easiest appears to be wrapping your logic in a React fragment like so:

<>                {/*<-------- wrap in fragment */}
{!!error.message && (
  <Text>{error.message}</Text>
)}
</>               {/*<-------- close the fragment */}

Related Questions

Upvotes: 34

Related Questions