Is there a way to apply TypeScript assuming a variable's type after a function checking?
For example:
const variable: string | undefined = someFunction()
variable && setAppTitle(variable) // getting sure that a variable is a defined string
But in my app I have the following code:
const [data, setData] = useState<ResponseData>() // data variable is of type ResponseData | undefined
const [isLoading, setIsLoading] = useState<boolean>(true)
useEffect(() => {
// load data on mount
onInit()
async function onInit() {
const response = await fetch("https://example.com")
const data = await response.json()
setData(data)
setIsLoading(false)
}
}, [])
const isLoaded = (): boolean => {
// some complex logic to check if the data is loaded and correct
return !isLoading && !!data && Object.keys(data).length > 0
}
return (
<>
<div>My data title:</div>
{isLoaded() && <div>{data.title}</div>} // error! data could be "undefined"
</>
)
So there is entire function checking a bunch of stuff.
Is there a way to point out to TypeScript that I have checked if the data
variable is not undefined
other than using data!.title
?
I can't afford putting the whole checking line into the JSX as this would blur the code and there is too much stuff going on. Or maybe there is a better way around this?
CodePudding user response:
You can use https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
But you need to pass the variable
const isLoaded = (response: ResponseData | undefined): response is ResponseData => {
// some complex logic to check if the data is loaded and correct
return !isLoading && !!data && Object.keys(data).length > 0
}
return (
<>
<div>My data title:</div>
{isLoaded(data) && <div>{data.title}</div>} // error! data could be "undefined"
</>
)
But I would personally use the isLoaded state for that and force data always to have value (and errors too):
const [error, setError] = useState<string>();
const [data, setData] = useState<ResponseData>({} as ResponseData);
const [isLoading, setIsLoading] = useState<boolean>(true);
return (
<>
<div>My data title:</div>
{isLoading && <div>Data is loading</div>}
{error && <div>Error loading data. {error} </div>}
{!isLoading && !isError && <div>{data.title}</div>}
</>
)