0

I can't get my typescript interface to work with a react tsx component. I've got all the items inside an array of objects with possible sub items.

I've tried lots of different interfaces but always ended up with some kind of error, currently only the map function seems to not work with the interface. (I'm a noob and started using typescript yesterday for the first time :P)

enter image description here

interface NavListItem {
  ID: string;
  title: string;
  path_url: string;
  child_items?: NavListProps;
}

interface NavListProps {
  items: { [index: number]: NavListItem };
}

const NavList: React.FC<NavListProps> = ({ items }) => {
  return items.map(item => (
    <li key={item.ID}>
      <Link to={item.path_url}>{item.title}</Link>
      {item.child_items ? (
        <ul>{<NavList items={item.child_items} />}</ul>
      ) : null}
    </li>
  ));
};

My goal is to have no errors and a working component. I appreciate all the help!

josias
  • 1,326
  • 1
  • 12
  • 39

1 Answers1

1

If items is supposed to be an array, then the correct interface for the props is this:

interface NavListProps {
  items: NavListItem[];
}

As you've defined it, items is an object with arbitrary key value pairs, who's only restriction is that the keys must be numbers, and the values must be NavListItems. This doesn't mean it's an array though, so typescript is pointing out that that type does not have a map method on it.

A couple of additional issues (as discussed in the comments):

1) When you recursively render NavList, you're passing in item.child_items, but that's defined to be a single NavListProps, when instead it should be a NavListItem[]:

interface NavListItem {
  ID: string;
  title: string;
  path_url: string;
  child_items?: NavListItem[];
}

2) Your component is returning just an array, but this should instead be wrapped in a Fragment:

const NavList: React.FC<NavListProps> = ({ items }) => {
  return (
    <React.Fragment>
      {items.map(item => (
        <li key={item.ID}>
          <Link to={item.path_url}>{item.title}</Link>
          {item.child_items ? (
            <ul>{<NavList items={item.child_items} />}</ul>
          ) : null}
        </li>
      }
    </React.Fragment>
  ));
};

P.S.: if your build is set up correctly, it's possible to do <></> as shorthand for <React.Fragment></React.Fragment>

Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
  • Like this I'm getting an error from the React.FC type which is something like: `type /thefunction/ is not assignable to type FunctionComponent. Type Element[] is missing the following properties from type ReacElement any type, props, key...` – josias Jul 13 '19 at 17:46
  • Oh, i think it doesn't like that you're returning an array. Wrap it in a fragment, as in `return {items.map(item => /* etc */)}` – Nicholas Tower Jul 13 '19 at 17:48
  • That seems to have worked, only there is an error now on the call to itself for the prop (inside the ul) saying `Type 'NavListProps' is missing the following properties from type NavListItem[]: length, pop, push, concat and 28 more `... I'm trying to create a recursive list – josias Jul 13 '19 at 17:58
  • Your interface for NavListItem defines item.child_items to be a *single* NavListItem, not an array of them. If it's actually an array, change the interface. If it's truly a single one, then wrap it in an array before passing it to the NavList component (or i suppose rewrite NavList to accept either an array or a single, but that'd be more work) – Nicholas Tower Jul 13 '19 at 17:59
  • Alright, thank you very much, I changed the line from `child_items?: NavListProps;` to `child_items?: NavListItem[];` and it seems to work now :) – josias Jul 13 '19 at 18:02
  • Maybe add the Fragment and child_items part to your answer so I can accept it – josias Jul 13 '19 at 18:08