简体   繁体   中英

Converting JavaScript object destructuring to Typescript in arrow function

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>

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM