0

I have converted the following arrow function destructure to Typescript, however I don't understand how to interpret the last item: icon: Icon. This item is not imported or declared.

Original JavaScript:

const NavbarDropdown = ({
  children,
  count,
  showBadge,
  header,
  footer,
  icon: Icon
}) => (
  <UncontrolledDropdown nav inNavbar className="mr-2">
    <DropdownToggle nav className="nav-icon dropdown-toggle">
      <div className="position-relative">
        <Icon className="align-middle" size={18} />

Converted to TypeScript except an error with {icon: Icon} where Icon is neither imported nor declared anywhere except within the body of the function:

const NavbarDropdown = (
    {children} : {children: string},
    {count} : {count: number},
    {showBadge} : {showBadge: boolean},
    {header} : { header: string},
    {footer} : { footer: string},
    {icon: Icon},
  ) => (
    <UncontrolledDropdown nav inNavbar className="mr-2">
      <DropdownToggle nav className="nav-icon dropdown-toggle">
        <div className="position-relative">
          <Icon className="align-middle" size={18} />

Update: I understand what this group is referring to regarding Icon/icon and I still cannot find anywhere Icon gets imported or declared. As suggested here is the code snippet of the NavbarDropdown call:

  <Collapse navbar>
    <Nav className="ml-auto" navbar>
      <NavbarDropdown
        header="New Messages"
        footer="Show all messages"
        icon={MessageCircle}
        count={messages.length}
        showBadge
      >
        {messages.map((item, key) => {
          return (
            <NavbarDropdownItem
              key={key}
              icon={
                <img
                  className="avatar img-fluid rounded-circle"
                  src={item.avatar}
                  alt={item.name}
                />
              }
              title={item.name}
              description={item.description}
              time={item.time}
              spacing
            />
          );
        })}
      </NavbarDropdown>
2
  • 1
    Your TypeScript version has a completely different signature to the JavaScript version. Try using const NavbarDropdown: React.FC<MyPropsInterface> = ... Commented Feb 3, 2021 at 11:08
  • 1
    {children, count,...}: {children:string, count:number,... What you have done instead is create a function multiple parameters, instead of a function with 1 parameter that you destruct. Commented Feb 3, 2021 at 11:09

1 Answer 1

2

Two issues that seem to stand out:

  1. Your issue with Icon.

  2. That isn't how you define the type of a single destructured parameter

Re #1, you've said:

Converted to TypeScript except an error with {icon: Icon} where Icon is neither imported nor declared anywhere except within the body of the function

In the JavaScript version, icon/Icon looks like this:

const NavbarDropdown = ({
  children,
  count,
  showBadge,
  header,
  footer,
  icon: Icon
}) => (

That icon: Icon looks a lot like a TypeScript type, but it isn't. It's part of the destructuring. It takes the value of the icon property on the object and assigns it to an Icon identifier. It's just as though you had this:

const NavbarDropdown = (props) => {
    let children = props.children;
    // ...
    let Icon = props.icon;

That so the identifier used for it within the function starts with an uppercase character so it can be used as a React component in JSX: <Icon className="align-middle" size={18} /> (If you had <icon .../> instead, it would be an HTML element, not a React component.)

Re #2: Your code id destructuring a series of parameters, one for each property. Instead, you put the type after the destructuring {}s:

const NavbarDropdown = ({
  children,
  count,
  showBadge,
  header,
  footer,
  icon: Icon
}: {
  children: string;
  count: number;
  showBadge: boolean;
  header: string;
  footer: string;
  icon: React.Component;
}) => (
  <UncontrolledDropdown nav inNavbar className="mr-2">
    <DropdownToggle nav className="nav-icon dropdown-toggle">
      <div className="position-relative">
        <Icon className="align-middle" size={18} />
        ...

Or declare a type you can reuse (it may also help with clarity), and then use that type name:

interface NavbarDropdownProps {
  children: string;
  count: number;
  showBadge: boolean;
  header: string;
  footer: string;
  icon: React.Component;
}
const NavbarDropdown = ({
  children,
  count,
  showBadge,
  header,
  footer,
  icon: Icon
}: NavbarDropdownProps) => (
  <UncontrolledDropdown nav inNavbar className="mr-2">
    <DropdownToggle nav className="nav-icon dropdown-toggle">
      <div className="position-relative">
        <Icon className="align-middle" size={18} />
        ...

But specifically when typing a React functional component, React provides a useful type: React.FunctionComponent or its shorter alias React.FC (most people use the latter):

interface NavbarDropdownProps {
  children: string;
  count: number;
  showBadge: boolean;
  header: string;
  footer: string;
  icon: React.Component;
}

const NavbarDropdown: React.FC<NavbarDropdownProps> = ({
  children,
  count,
  showBadge,
  header,
  footer,
  icon: Icon
}) => (
  <UncontrolledDropdown nav inNavbar className="mr-2">
    <DropdownToggle nav className="nav-icon dropdown-toggle">
      <div className="position-relative">
        <Icon className="align-middle" size={18} />
        ...

Note that since you're telling TypeScript the type of the props by using a generic parameter with React.FC, you don't have to supply the type in the parameter list anymore.

But normally, children in a functional component should be of type ReactNode, not string. string will work because ReactNode is a union type and string is one of its parts, but it will narrow the type of the children where normally you want to allow the full range of children.

Sign up to request clarification or add additional context in comments.

11 Comments

The only issue with React.FC, here though is that the OP has decided children is a string instead of ReactNode.
Icon would not be of type Icon, but something like React.Component
@Keith - Good point about children, thanks. Although they could require it just be a string, since ReactNode is a union and one of the things in the union is string, they're allowed to narrow it. :-)
@Keith - It was a really good point to raise. :-)
@T.J. Crowder, et al. - thank you for clarifying how to destructure a series of parameters. I saw a few examples in other threads but clearly misinterpreted.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.