Ok so here is my issue.
interface Example {
name: string
}
const [name, setName] = useState<Example>()
const fetchedData = fetch().json()
setName(fetchedData)
the type of name is <Example | undefined> at the moment which messes with my components downstream.
when i fetch the data i also set a isFetched variable to true, and the components that rely on my data only render in this case, so the variable "name" is never undefined when passing it to the respective component. However now all my downstream components that rely on the .name property are complaining, because the variable could be undefined
CodePudding user response:
This is because of how TypeScript works. Basically, even though you know that name
is never undefined
when isFetched
is true, TypeScript's static analysis is not clever enough to work this out. As far as its concerned, name
could be undefined even though isFetched
is true. Typescript does not know the relationship between these variables (unless you tell it, see option 3).
Whilst this seems frustrating, it actually makes sense. Imagine if later down the line you or someone else set isFetched
to true but forgot to set the data...your app would error. TypeScripts purpose is to lock down these possibilities to give you stronger guarantees. There's no problem now, but as your code grows, it could be a problem in the future.
You have 3 options
- Change your conditional that checks
isFetched
to also checkname
. For exampleisFetched && name !== undefined
. - Tell typescript you are sure about this and before you pass
name
as a prop to the sub components, add a non-null assertion operator after it. I.e.name!
. Be careful with using this. Whilst its cheap and very tempting, you are basically opting out of TypeScript safety so using it routinely can degrade the robustness of your code unless youre sure. - The fancy option -- merge your fetching and data vars into one object, and let typescript know what the valid combinations are using a discriminated union:
type Example = {name: string, isFetched : true} | {name: undefined, isFetched: false}
// ...
const [name, setName] = useState<Example>({name: undefined, isFetched: false})
// ...
setName({name: fetchedData, isFetched: true })
// Doing a conditional on `isFetched` will mean everything inside that if-block will have `name` as string and not string or undefined.