How to filter only specific components in Typescript

Issue

During the recent code refactoring, I have a question that I do not understand.
First of all, here’s what I’ve been working on:

// CustomTracks.tsx
import { faList } from '@fortawesome/free-solid-svg-icons';
import styled from 'styled-components';
import Icon from '../../../../components/atom/Icon';
import LinkElement from '../../../../components/atom/LinkElement';

const MenuItem = styled.li`
  * {
    width: 25px;
    height: 25px;
  }
  Link,
  LogoutButton {
    padding: 5px;
  }
`;

function CustomTracks() {
  return (
    <MenuItem>
      <LinkElement
        LinkStyle={undefined}
        linkText={<Icon icon={faList} />}  // Attributes you want to filter on
        title="Favorite track list"
        to="/tracks/custom"
      />
    </MenuItem>
  );
}

export default CustomTracks;
// Link.tsx
import { Link, LinkProps } from 'react-router-dom';
import { StyledComponent } from 'styled-components';

export interface LinkElementProps {
  to: string;
  linkText: string | JSX.Element; // what actually worked
  title?: string;
  LinkStyle?: StyledComponent<
    React.ForwardRefExoticComponent<
      LinkProps & React.RefAttributes<HTMLAnchorElement>
    >,
    any,
    {},
    never
  >;
}

function LinkElement({ to, title, linkText, LinkStyle }: LinkElementProps) {
  return LinkStyle ? (
    <LinkStyle to={to} title={title ? title : ''}>
      {linkText}
    </LinkStyle>
  ) : (
    <Link to={to}>{linkText}</Link>
  );
}

export default LinkElement;

I want the linkText property of the Link component to accept only string and Icon components. But as you can see, the type of linkText is string | Since it is JSX.Element, it is designed to allow any component, not just the Icon component.
I don’t know what to do to solve this.

Solution

This is not possible in TypeScript because there is no way to know the type of the object passed, since it all comes down to a JSX.Element. The only workaround that comes in mind would be to display an error message at runtime, and return null or something else, like this:

function LinkElement({ to, title, linkText, LinkStyle }: LinkElementProps) {
  if (
    typeof linkText !== 'string' &&
    'type' in linkText &&
    linkText.type.name !== Icon.name
  ) {
    console.log('Wrong component type for LinkElement, only accepting Icon!');
    return null
  }
  return LinkStyle ? (
    <LinkStyle to={to} title={title ? title : ''}>
      {linkText}
    </LinkStyle>
  ) : (
    <Link to={to}>{linkText}</Link>
  );
}

There is an open issue regarding this here.

Answered By – Mentlegen

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published