Home > Blockchain >  Smart assuming of variable type
Smart assuming of variable type

Time:09-09

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>}
    </>
)
  • Related