Home > Enterprise >  Only allow a specific element as a prop checked by Typescript
Only allow a specific element as a prop checked by Typescript

Time:05-25

How to define that only a specific element type is allowed to be passed as a prop to a component?

Let's say I have a component which has a prop divider and it should be typed in a way so that only <svg> elements can be passed to this prop.

import { ElementType, ReactElement } from 'react';

type Props = {
  divider: ReactElement<'svg'>;
};

export function SVGClipPathSection({ divider: Divider }: Props) {
  return (
    <div>
      <Divider />
      Blub
    </div>
  );
}

This leads to

JSX element type 'Divider' does not have any construct or call signatures.ts(2604)

What would be the appropriate type?

Update #1

type Props = {
  divider: SVGElement;
};

export function SVGClipPathSection({ divider }: Props) {
  return <div>{divider}</div>;
}

This leads to:

error TS2322: Type 'SVGElement' is not assignable to type 'ReactNode'.
  Type 'SVGElement' is missing the following properties from type 'ReactPortal': key, type, props

6   return <div>{divider}</div>;
                ~~~~~~~~~

  node_modules/@types/react/index.d.ts:1375:9
    1375         children?: ReactNode | undefined;
                 ~~~~~~~~
    The expected type comes from property 'children' which is declared here on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'

CodePudding user response:

I don't think what you want to do is possible.

You can, however, traverse your component array and filter those of the type you want, possibly raising a warning if there are children of unwanted type.

const filtered = divider.filter(e => React.isValidElement(e) && e.type === 'svg');

if (filtered.length !== divider.length) {
  console.warn('divider elements must be of type <svg/>');
}

const typed = filtered as ReactElement<React.ComponentProps<'svg'>>[];

Edit: You have a good explanation of why what you want might not be possible in this answer:

Any react functional component is just a function that has a specific props type and returns JSX.Element. This means that if you render the component before you pass it a child, then react has no idea what generated that JSX at all, and just passes it along.

And problem is that you render the component with the <MyComponent> syntax. So after that point, it's just a generic tree of JSX nodes.

CodePudding user response:

You can use HTML Element interfaces to define element type. So for your example, you can define the divider type as SVGElement. Please refer this, https://developer.mozilla.org/en-US/docs/Web/API#interfaces

type Props = {
  divider: SVGElement;
}
  • Related