Search code examples
javascripttypescriptjsxsolid-js

How to bind certain type to a function argument that expects JSX element


I have a component that returns its own props (props are of type title: string; children: JSXElement) as an JSX element.

import { JSXElement } from 'solid-js';

const Tab = (props: { title: string; children: JSXElement }) => props as unknown as JSXElement;

export default Tab;

Now, I have a component where I am accessing those specific props by using a children helper from SolidJS:

import { For, Show, children, createSignal } from 'solid-js';

import { TabTitle } from '../';
import toArray from '../../utils/toArray';
import { Container, TabsUnorderedList } from './styles';

const Tabs = (props: ITabs) => {
  const [selectedTab, setSelectedTab] = createSignal(0);

  const c = children(() => props.children);

  return (
    <Container>
      <TabsUnorderedList>
        <For each={toArray(c())}>
          {(tab: any, index) => (
            <TabTitle
              title={tab.title}
              index={index}
              activeTab={setSelectedTab}
            />
          )}
        </For>
      </TabsUnorderedList>
      <For each={toArray(c())}>
        {(tab: any, index) => (
          <Show when={index() === selectedTab()} fallback={null}>
            {tab.children}
          </Show>
        )}
      </For>
    </Container>
  );
};

export default Tabs;

How can I bind props from Tab component (title: string; children: JSXElement) on the tab argument of callback function?

The tab is of type JSXElement. I tried using the ComponentProps, a helper function from SolidJS that takes the props of the passed component and returns its type but I didn't have any success with doing that.

I also tried using unknown, extending the existing types with unknown, using <T,>(props: { title: string; children: JSXElement } & T), adding tab prop in hopes that will solve it but to no avail.


Solution

  • I managed to do it in a following way:

    First I declared a custom type checking function:

    export default (x: any): x is ITab => x.title && x.children;
    

    where ITab is { title: string; children: JSXElement }.

    Then I just added that type checking where necessarry:

            <For each={toArray(c())}>
              {(tab, index) =>
                isOfTabType(tab) && (
                  <TabTitle
                    title={tab.title}
                    index={index}
                    activeTab={setSelectedTab}
                  />
                )
              }
            </For>
    

    Hopefully this can help someone in the future!!