You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Types related to ReactNode inferred incorrectly when using React 17 #50659
In non-strict mode, the return type of test1 and test3 is {}[], and the other two produces ReactNode[].
In strict mode, the return type of test1 is ({} | null)[], test2 and test3 produces ({} | null | undefined)[], and the remaining one produces ReactNode[].
Expected behavior
The return type of all of the 4 functions above being ReactNode[].
{} is the supertype of everything except null and undefined. So ReactNode reduces to {} | null | undefined (which is actually interchangeable with unknown in 4.8) when you poke it enough to force subtype reduction, e.g., in narrowing.
To get a feel for how this works, we can just look at test1. Since fallback has a default value, it actually narrows down from ReactNode. We know that fallback can be any one of the things ReactNode can be, except actually not undefined, since we can tell <div /> is not undefined. So we remove undefined from the possible return type of the map callback:
and then do subtype reduction to remove redundant types, which leaves us with {} | null—that is, any possible value except undefined.
So this is actually a more specific type than ReactNode, which is equivalent to unknown. TypeScript is helping you narrow down what’s actually possible for this function to return. This is a feature. Operating on a pretty useless type, ReactNode. Literally anything can be a ReactNode—don’t let it trick you with its specific-sounding name—so it’s not surprising the return types are also pretty vague.
You’re free to annotate the return types of your functions as ReactNode if you want them to remain unknown.
Andrew Branch (@andrewbranch) Oh I get it now. I missed the typing of ReactFragment. I should have inspected the type more carefully. A tooltip like type ReactNode = unknown would be helpful, nevertheless.
So this must be a DefinitelyTyped issue then. Does anyone know how come the definition of ReactNode in React 17 is so loose? (I understand changing this breaks things, but I am still looking for a (historical) reason.)
Well I guess I’m going to migrate to React 18 when I get some time.
Activity
andrewbranch commentedon Sep 9, 2022
ReactNodeisand
ReactFragmentis{} | Iterable<ReactNode>.{}is the supertype of everything exceptnullandundefined. SoReactNodereduces to{} | null | undefined(which is actually interchangeable withunknownin 4.8) when you poke it enough to force subtype reduction, e.g., in narrowing.To get a feel for how this works, we can just look at
test1. Sincefallbackhas a default value, it actually narrows down fromReactNode. We know thatfallbackcan be any one of the thingsReactNodecan be, except actually notundefined, since we can tell<div />is notundefined. So we removeundefinedfrom the possible return type of themapcallback:and then do subtype reduction to remove redundant types, which leaves us with
{} | null—that is, any possible value exceptundefined.So this is actually a more specific type than
ReactNode, which is equivalent tounknown. TypeScript is helping you narrow down what’s actually possible for this function to return. This is a feature. Operating on a pretty useless type,ReactNode. Literally anything can be aReactNode—don’t let it trick you with its specific-sounding name—so it’s not surprising the return types are also pretty vague.You’re free to annotate the return types of your functions as
ReactNodeif you want them to remainunknown.graphemecluster commentedon Sep 10, 2022
Andrew Branch (@andrewbranch) Oh I get it now. I missed the typing of
ReactFragment. I should have inspected the type more carefully. A tooltip liketype ReactNode = unknownwould be helpful, nevertheless.So this must be a DefinitelyTyped issue then. Does anyone know how come the definition of
ReactNodein React 17 is so loose? (I understand changing this breaks things, but I am still looking for a (historical) reason.)Well I guess I’m going to migrate to React 18 when I get some time.
typescript-bot commentedon Sep 12, 2022
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.