coma
coma

Reputation: 16649

Pick a type from one of the props

I've a List component

type Option = {
  id: string;
};

type Props<O> = {
  options?: O[];
  option?: JSXElementConstructor<O>;
};

const List = <O extends Option>(props: Props<O>) => ...

and some option components like this one

type Props = {
  label: string;
  icon?: ElementType;
  onClick?: () => void;
};

const BasicOption = (props: Props) => ...

in order to get the right type I have to do this

const Example = () => {
  const options = [
    { id: 'a', label: 'Label A', icon: PlaceholderIcon },
    { id: 'b', label: 'Label B', icon: PlaceholderIcon },
    { id: 'c', label: 'Label C', icon: PlaceholderIcon },
    { id: 'd', label: 'Label D', disabled: true },
    { id: 'e', label: 'Label E' },
  ];

  return (
    <List<typeof options[number]>
      options={options}
      option={BasicOption}
    />
  );
};

is there a way to get the right typing directly from the array in order to avoid the <typeof options[number]> part?

Working example: https://codesandbox.io/s/vigorous-hopper-i41uj?file=/src/App.tsx

Upvotes: 3

Views: 903

Answers (1)

You need infer options prop and bind it with option.

import React, { JSXElementConstructor, ElementType } from "react";


export type BasicProps = {
  label: string;
  icon?: ElementType;
  onClick?: () => void;
};

export const BasicOption = ({ label, icon: Icon, onClick }: BasicProps) => (
  <div onClick={() => onClick?.()}>
    {label}
    {Icon && <Icon />}
  </div>
);


export type Option = {
  id: string;
};

export const List = <Opt extends Option, Options extends Opt[]>({
  options,
  option: Option
}: {
  options: [...Options],
  // same as typeof options[number] but Options is infered
  option: JSXElementConstructor<[...Options][number]>;
}) => (
  <div>
    {Option &&
      options &&
      options.map((option) => <Option key={option.id} {...option} />)}
  </div>
);

export default function App() {
  const options = [
    { id: "a", label: "Label A" },
    { id: "b", label: "Label B" },
    { id: "c", label: "Label C" },
    { id: "d", label: "Label D", disabled: true },
    { id: "e", label: "Label E" }
  ];

  return (
    <List options={options} option={BasicOption} />
  );
}

Playground

You can find more about function arguments inference in my blog

Upvotes: 1

Related Questions