-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: react props with children didn't allow for multiple children #4493
Conversation
in some weird cases this led to wrong errors being displayed
@@ -748,7 +748,7 @@ declare namespace React { | |||
): void; | |||
|
|||
export type PropsWithChildren<P = unknown> = P & { | |||
children?: preact.ComponentChild | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
React's type seems to be ReactNode
, rather than ReactNode[]
(which they do use elsewhere). As ComponentChildren
is ComponentChild | ComponentChild[]
, I'm not quite sure this change is correct.
Does this work correctly in React? It's very possible we have an issue elsewhere in the types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, yes and no: ReactNode
is an alias of ComponentChild
but does not include Iterable<ComponentChild>
like reacts ReactNode
does does and simply extending that union in preact/compat
only isn't possible the way it's written currently. I thought this is the minimal change but I agree, imho the correct change would be to drop ComponentChildren
, rename ComponentChild
to PreactNode
and include Iterable<PreactNode>
in it. But that's not exactly a small change...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't support iterables, the type is correct there FWIW.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wdym "we don't support iterables"? You have arrays in your types, arrays are iterables? Or did you mean PreactNode[]
instead of Iterable<PreactNode>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as for "does this work correctly in react" - well, it's a type change? Ofc react still works. And if you're using react (not preact) then this issue wouldn't arise in the first place?
Kinda confused what you mean to say here... changing this solves the described issue and doesn't break anything else in the rather large project that I'm currently migrating from react to preact, so... yes? It works in a "react like context"?
EDIT: ah, you probably meant "does the issue above arise in react" - no, it doesn't probably because ReactNode
includes iterables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wdym "we don't support iterables"? You have arrays in your types, arrays are iterables?
Arrays are iterable, but not all iterables are arrays; Map
, Set
, Uint32Array
, and other structures are iterable too.
as for "does this work correctly in react" - well, it's a type change? Ofc react still works.
I'm asking if your type (for your component) works in React -- I've always used ReactNode[]
/ComponentChildren
for multiple nodes, personally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it does, we're using this heavily throughout multiple large projects and never had any problems until we migrated to preact (which we did because react is basically incompatible with CEs and probably will continue to be)
After taking a look, I don't think this is the correct solution. The generic of interface CompProps {
foo: string;
}
function Comp(_: React.PropsWithChildren<CompProps>) {
return <></>;
} ...then the issue will go away, and multiple children are a-okay. I believe this is because any union of That being said, there are still some weird interactions that I don't quite understand. If we take the component definition above, and use it like so: const x = (
<Comp foo="bar" baz={0}>
<div />
<div />
</Comp>
); ...the I'm wondering if we do have a deeper types issue somewhere, as this seems a bit funky & confusing indeed. Maybe |
sorry, that is wrong. Also I must point out that your reproduction is flawed in that it produces exactly the same issue as I originally described: you're passing multiple children which is fine as long as there aren't excess props. As soon as the excess prop appears the children must be of type string (since that is the only type that allows for multiple lines to appear as a single child maybe?) The issue at hand is that preacts definition of a "Node" diverges from reacts definition - adding Also, the "we don't support iterables" thing might be good to mention in the "differences to react" section. But I have to admit I've never seen the use of other iterables as children though I can imagine some use cases for that... Btw, in said "differences to react" section I just read it states
which should (currently) be appended with: "...unless you switch from react and have components that utilize |
My reproduction assumes you want to adhere to your types, yes. Your example should error, albeit, produce a different message. The reproduction was intended to show corrected use.
Fair enough, we'll land this for now & address it later if it causes issues. Generally, we try to stick to React's types as closes as possible for easy comparison later -- ideally TS would have some better mechanism for patching/overriding types, but as it is, we have to re-implement all those asked for. That's why I'm somewhat wary of altering too much.
This is the more interesting issue. |
after some digging I have a hunch (at least) as to why this is happening. Preacts JSX runtime is typing the |
damnit, I just wanted to write "if this were my project I'd hold off merging this until I have some clarification from the typescript team"^^ |
Huh, interesting.
Shouldn't be a problem; this will go out next release, at the very least fixing your components/app. If people do have issues or we find out more later, we can always revert or make changes. Not a worry. |
in some weird cases this led to wrong errors being displayed. Sadly I don't fully understand why, but defining a component (e.g. a CE) with React.PropsWithChildren and providing multiple children as well as non-existent props led to typescript complaining about
Element is not assignable to string
.So e.g.
is invalid.
however yields the expected error that
foo
is not a prop onComp
.While I do think that this is not preacts error I don't see why intrinsic elements should be able to receive multiple children while explicitly typed components should not?