David Gomes
David Gomes

Reputation: 5825

Component which prop must be an array of specific React elements

I'm trying to write a component which accepts an array of instances of another component as a prop. That component is MyComponent, which accepts an array of instances of Title as one of its arguments. However, I am not able to get TypeScript to type check this:

import * as React from "react";


type TitleProps = {
    name: string;
};

function Title(props: TitleProps) {
    return null;
}

type MyComponentProps = {
    titles: Array<React.ReactElement<TitleProps>>;
}

function MyComponent({ titles }: MyComponentProps) {
    return <div>{titles}</div>;
}

function OtherComponent(props: {}) { return null }

const shouldError = <MyComponent titles={[ <div/>, <OtherComponent/> ]} />;
const shouldNotError = <MyComponent titles={[ <Title name="hi"/>, <Title name="hi2"/>, ]} />;

As you can see, I am able to pass whatever I want to the titles prop, not just instances of <Title/>.

TypeScript Playground URL

Upvotes: 13

Views: 1372

Answers (2)

Subrato Pattanaik
Subrato Pattanaik

Reputation: 6049

The feature of type safety for JSX.Element in Typescript is not available to us yet.

We had this similar scenario before and what we did is instead of having an array of JSX.Element, we use an array of props that the same element can have. Then during render, we simply map the array of props to the array of JSX.Element.

import * as React from "react";

type TitleProps = {
  name: string;
};

function Title(props: TitleProps) {
  return null;
}

type MyComponentProps = {
  titles: TitleProps[];
};

function MyComponent({ titles }: MyComponentProps) {
  return (
    <React.Fragment>
      {titles.map(titleProps => (
        <Title {...titleProps} />
      ))}
    </React.Fragment>
  );
}

// no errror
const shouldNotError = (
  <MyComponent titles={[{ name: "React" }, { name: "fixme" }]} />
);

//error
const shouldError = (
  <MyComponent titles={[{ other: "don't use this" }, { another: "help" }]} />
);

Upvotes: 4

Codebling
Codebling

Reputation: 11382

TypeScript is not a silver bullet. There are a number of cases in which guaranteeing 100% type safety requires the introduction of additional complexity in the code, or is simply impossible. You have encountered one such situation.

There is an open GitHub issue on the subject of typing JSX elements. Your best bet is to wait for TypeScript to introduce a specific mechanism for this.

Otherwise (from Matt McCutchen's answer):

all JSX elements are hard-coded to have the JSX.Element type, so there's no way to accept certain JSX elements and not others. If you wanted that kind of checking, you would have to give up the JSX syntax, define your own element factory function that wraps React.createElement but returns different element types for different component types, and write calls to that factory function manually.

So as of right now there is no way to ensure type safety for any JSX elements

Upvotes: 4

Related Questions