3

Im having some trouble defining a generic for this specific scenarion.

I have multiple React components defined like this:

type ComponentFunction<T> = (props: T) => void;

interface FooProps {
  bar: string;
}

const Foo: ComponentFunction<FooProps> = ({ bar }) => {};

and I want to have an interface that would look something like this:

interface Component<T> {
  element: ComponentFunction<T>;
  props: T;
}

what I would like is to have an array of those components without having to define the generic for each and every one of them. It would just be automatically "detected" from the passed element, for example:

const components: Component<?>[] = [
  {
    element: Foo,
    props: {
      bar: "bar"
    }
  },
  ...
]

Is this even possible?

Edit 1:
Replaced React.FC with a more generic example ComponentFunction.

Solution 1:

const componentArray = <T extends any[]>(array: { [I in keyof T]: Component<T[I]> }) => array;

const components = componentArray([
  {
    element: Foo,
    props: {
      bar: "string"
    }
  },
  ...
]);

Solution 2:

type SomeComponent = <R>(cb: <T>(component: Component<T>) => R) => R;

const someComponent = <T,>(component: Component<T>): SomeComponent => cb => cb(component);

const components: SomeComponent[] = [
  someComponent({ element: Foo, props: { bar: "string" } }),
  ...
];

Thanks to @jcalz for providing the solutions.

6
  • Not really sadly. Maybe the example was too React specific. I will update it. The biggest issue I'm having with this is how to avoid defining the generic here Component<?>[] Commented Jun 14, 2022 at 17:55
  • 1
    This is a canonical use case for existentially quantified generics, which TS doesn't have direct support for (but neither do most languages, so it's not a problem with TS per se). Your choices here are either to make everything generic and map over an array type, or to emulate existentials. This playground link shows both approaches. Commented Jun 14, 2022 at 18:12
  • 1
    See this Q/A for more information. Commented Jun 14, 2022 at 18:15
  • @jcalz Thank you! Visually it's not the nicest solution but sadly it's the only solution. For my case I prefer to make everything generic. Also very nice explanation on this Q/A! Commented Jun 14, 2022 at 18:31
  • @jcalz Interesting issue with both solutions. If I define FooProps as interface FooProps { bar: "a" | "b" | "c" } I get an error Type '"string"' is not assignable to type '"a" | "b" | "c"'. Commented Jun 14, 2022 at 19:18

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.