Skip to content
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

Bug: Constraining children to type has no effect #27545

Closed
speculees opened this issue Oct 18, 2023 · 5 comments
Closed

Bug: Constraining children to type has no effect #27545

speculees opened this issue Oct 18, 2023 · 5 comments
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@speculees
Copy link

I need to set a constraint on parent component, so only children of certain type are allowed. Maybe this is intended behavior, but if so how do you set constraint?

React version: 18.2.0

Steps To Reproduce

  1. Create a component with children property and use generic type React.ReactElement

    to constrain what children are allowed to be passed

  2. Observe that you can still add any child as if React.ReactElement is used

Link to code example: https://codesandbox.io/s/react-typescript-forked-wzkkvl?file=/src/App.tsx

The current behavior

Allows the children of parent to be of any type

The expected behavior

Parent component should be marked with error "Types of property 'children' are incompatible." if other component is passed as child

@speculees speculees added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Oct 18, 2023
@sophiebits
Copy link
Collaborator

@eps1lon Remind me if this is a limitation of core TypeScript?

@Youngemmy5956
Copy link

Youngemmy5956 commented Oct 21, 2023

import React, { ReactElement, ReactNode } from "react";

type ValidChild = ReactElement<{ customProp: string }>;

type ParentProps = {
children: ValidChild | ValidChild[];
};

const Parent: React.FC = ({ children }) => {
if (!Array.isArray(children)) {
children = [children];
}

const invalidChildren = children.filter(
(child) => child.type !== ChildComponent
);

if (invalidChildren.length > 0) {
throw new Error("Invalid child components in Parent");
}

return

{children}
;
};

type ChildProps = {
customProp: string;
};

const ChildComponent: React.FC = ({ customProp }) => {
return

{customProp}
;
};

export default function App() {
return (


Parent should complain



);
}

Youngemmy5956 added a commit to Youngemmy5956/react that referenced this issue Oct 21, 2023
Bug: Constraining children to type has no effect facebook#27545
@speculees
Copy link
Author

import React, { ReactElement, ReactNode } from "react";

type ValidChild = ReactElement<{ customProp: string }>;

type ParentProps = { children: ValidChild | ValidChild[]; };

const Parent: React.FC = ({ children }) => { if (!Array.isArray(children)) { children = [children]; }

const invalidChildren = children.filter( (child) => child.type !== ChildComponent );

if (invalidChildren.length > 0) { throw new Error("Invalid child components in Parent"); }

return

{children}
;
};
type ChildProps = { customProp: string; };

const ChildComponent: React.FC = ({ customProp }) => { return

{customProp}
;
};
export default function App() { return (

Parent should complain

);
}

I was thinking about this approach, but this is would loop (filter) children in runtime instead of using typescript.

@Youngemmy5956
Copy link

Youngemmy5956 commented Oct 23, 2023

What about using this approach ??

This approach allows you to catch errors at compile time and ensures that the child components adhere to the expected props and structure.

import React, { ReactElement, ReactNode } from "react";

type ChildProps = {
customProp: string;
};

type ChildComponentProps = {
children: ReactNode;
customProp: string;
};

const ChildComponent: React.FC = ({ customProp }) => {
return

{customProp}
;
};

const Parent: React.FC = ({ children, customProp }) => {
return (


{customProp}

{children}

);
};

export default function App() {
return (




);
}

@eps1lon
Copy link
Collaborator

eps1lon commented Nov 29, 2023

It's a TypeScript limitation because <ValidChild /> will be any JSX element i.e. (< ValidChild />).type any not ValidChild. It would only work if you'd use a JSX factory directly i.e. createElement(ValidChild). In that case, TypeScript would recognize the real element type (createElement(ValidChild).type is ValidChild) and your parent could safely assume children: ValidChild is enforced at the type level.

But this shifts the burden to component consumers which defeats the purpose.

Closing in favor of microsoft/TypeScript#14729

@eps1lon eps1lon closed this as not planned Won't fix, can't repro, duplicate, stale Nov 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

No branches or pull requests

4 participants