Reputation: 677
I want to write a component that render its children only if some given prop is not null (e.g. to render a loader while fetching some data and update with the actual data when fetch completes). This is pretty simple:
import React, { ReactElement, ReactNode, useState } from 'react'
type TState = {
data: {
text: string
} | null
}
type NotNullProps = {
children: ReactNode
state: TState
}
const NotNull = ({ children, state }: NotNullProps) => {
if (!state.data) {
return null
}
return <>{children}</>
}
Now, what if I want to use this component this way?
const Example = () => {
const [state] = useState<TState>({ data: null })
return (
<NotNull state={state}>
<div>{state.data.text}</div> // <--- "Object is possibly 'null'."
</NotNull>
)
}
Typescript correctly signals that state.data
can be null because it can't infer what is happening in NotNull
, but state.data
will never be null since NotNull
checks for it.
I know this can be solved with render props but they add a bit of clutter to the code. So, is there a way to type NotNull
in such a way that typescript does not complains/can infer what is happening?
Here's a playground to try it: https://tsplay.dev/zwORWq
(Yes, I know about Suspense
and other stuff but it is not relevant to this question. I just want to know if there's a way to type this component in a way that typescript can infer that the value is not null (again, without render props)).
Upvotes: 1
Views: 302
Reputation: 3855
You will still have to check for whether state.data
is null
because even though your NotNull
component prevents the children from being rendered, your code will still result in a TypeError
with your usage if state.data
is null
, because the line state.data.text
in your code will produce TypeError: Cannot read property 'text' of null
when it is interpreted, whether or not the children render. I would suggest just changing the lines to:
return (
<NotNull state={state}>
<div>{state.data && state.data.text}</div>
</NotNull>
)
But of course, this would negate the need for the NotNull
component in the first place, except that it also prevents the <div>
in your example from being rendered
Upvotes: 1